{"id":20485293,"url":"https://github.com/runnable/shtub","last_synced_at":"2026-05-27T20:31:46.202Z","repository":{"id":70965114,"uuid":"50480046","full_name":"Runnable/shtub","owner":"Runnable","description":"Bash command stubbing","archived":false,"fork":false,"pushed_at":"2016-02-01T22:44:19.000Z","size":28,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-05T16:26:41.174Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Runnable.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-01-27T03:52:02.000Z","updated_at":"2016-01-28T19:04:55.000Z","dependencies_parsed_at":"2023-04-24T13:52:04.253Z","dependency_job_id":null,"html_url":"https://github.com/Runnable/shtub","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Runnable/shtub","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Runnable%2Fshtub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Runnable%2Fshtub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Runnable%2Fshtub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Runnable%2Fshtub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Runnable","download_url":"https://codeload.github.com/Runnable/shtub/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Runnable%2Fshtub/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33583394,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T16:29:15.184Z","updated_at":"2026-05-27T20:31:46.186Z","avatar_url":"https://github.com/Runnable.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shtub\nBash command stubbing\n\n[![Build Status](https://travis-ci.org/Runnable/shtub.svg?branch=master)](https://travis-ci.org/Runnable/shtub)\n\n## Overview\nshtub is a bash testing utility that allows for the creation of command stubs\nin a bash environment. A stub acts as a \"faux\" command which override the\ndefault behavior of a command and allows the programmer to set specific behaviors\n(such as output to `stdout` or `stderr`, return codes, etc.).\n\n## Installation\nTo install shtub in the current directory, run:\n```bash\ncurl -L https://raw.github.com/runnable/shtub/master/shtub.sh \u003e ./shtub.sh\n```\n\n## Example: using `shtub` with `shpec`\n```bash\n#!/bin/bash\nsource \"./shtub.sh\"\n\n# Testing a custom exponential back off function that executes commands\n# until they succeed...\ndescribe 'util/backoff'\n  # before\n    # Create a testing stub action that will fail on the first and second call,\n    # then finally succeed on the third call\n    stub::errors 'action' 'failed'\n    action::on_call 3 true\n\n    # Create a secondary stub action that fails further down the line\n    stub::errors 'action2' 'failed'\n    action::on_call 5 true\n\n    # Stub the `sleep` command so we can ensure it is being called\n    stub 'sleep'\n  # end\n\n  it 'should sleep between tries'\n    backoff action\n    assert sleep::called_twice\n  end\n\n  it 'should exponentially back-off on failure'\n    # Reset the action and sleep stubs so we can start fresh\n    action::reset\n    sleep::reset\n\n    # Perform the test\n    backoff action\n    assert sleep::called_with 2\n    backoff action2\n    assert sleep::called_with 8\n  end\n\n  # after\n    # Finally, after done testing restore the stubs to their original state\n    action::restore\n    sleep::restore\n  # end\nend\n```\n\n## Limitations\nThe library has two major limitations:\n\n1. You cannot stub internal library functions, variables, and commands\n2. You cannot stub bash built-ins\n\nWithout these two rules it would be incredibly easy to fork bomb yourself ;).\n\n## API Reference\nStub is used via a series of commands that mutate a given bash environment by\noverriding existing commands and adding specialized functions that allow you to\nmanipulate their stubbed behavior.\n\nThere are two types of commands that can be executed when using the library:\n\n- Setup commands: create stubs for commands in the shell environment\n- Stub commands: sugar commands that are \"attached\" to stubs that allow the user\n  to mutate their behavior and perform assertions based on various metrics\n  (number of times called, which parameters they were called with, etc.)\n\nIn this section we will cover each of the commands and provide usage examples.\n\n### Setup Commands\nThe only commands exposed are used to initialize stubs in the bash environment.\nIn addition to overriding the default behavior of the command the setup commands\nwill add a slew of special `::` methods that can then be called to get information\nabout how a stub was used.\n\n#### stub `\u003ccommand\u003e`\n\n- `\u003ccommand\u003e` - Name of the command to stub\n\n```bash\n# Stubs the `cat` command\nstub 'cat'\n```\n\nCreates a basic stub for the given command. The stub will have no effect and\nsimply return `0`. This method will also set a slew of methods that can be used\nto change the default behavior of the stub. See the `::` methods below for more\ninformation.\n\n#### stub::returns `\u003ccommand\u003e` `\u003coutput\u003e`\n\n- `\u003ccommand\u003e` - Name of the command to stub\n- `\u003coutput\u003e` - Output the stub should pipe to `stdout` when called\n\n```bash\nstub::returns 'cat' 'foobar'\ncat 'neat' # Outputs: foobar\n```\n\nCreates a stub for the given command that pipes the given output to `stdout`\nwhen the command is executed.\n\n#### stub::errors `\u003ccommand\u003e` `\u003coutput\u003e` `[code=1]`\n\n- `\u003ccommand\u003e` - Name of the command to stub.\n- `\u003coutput\u003e` - Output to pipe to `stderr`\n- `[code=1]` - Status code the stub should return (defaults to 1)\n\n```bash\nstub::errors 'echo' 'cannot echo?'\necho 'hello' # pipes 'cannot echo?' to stderr, returns code 1\nstub::errors 'neat' 'not a commmand' 127\nneat # pipes 'not a command' to stderr, returns code 127\n```\n\nCreates a stub for the given command that pipes the given output to `stderr` and\nreturns the given status code.\n\n#### stub::exec `\u003ccommand\u003e` `\u003cexec-command\u003e`\n\n- `\u003cexec-command\u003e` - Command or function to execute when the stub is called\n\n```bash\n# Direct command\nstub::exec 'curl' 'echo \"wowza\"'\n\n# Using a function\necho_stub() {\n  echo 'hello'\n  echo 'world'\n  return 1\n}\nstub::exec 'echo' echo_stub\n```\n\nCreates a stub for the given command that executes the given command string\nor function when the stub is called.\n\n### Stub Commands\nStub commands are special commands that are automatically added to the bash\nenvironment when creating a stub. They are also known as `::` commands because\neach one starts with the name of the stubbed command, followed by two colons,\nand then the name of the action to perform. For example:\n\n```bash\n# Generate a stub and all special stub commands for `ls`\nstub 'ls'\n\n# Now we can use the stub commands\nls::called_once\nls::restore\n```\n\nThe rest of this section details each of the special `::` commands that are\navailable to a stub.\n\n#### ::restore\n```bash\n# Create the stub\nstub 'echo'\necho 'Hi there' # Outputs nothing\n\n# Restore the original command\necho::restore\necho 'Hi there' # Works as expected\n```\n\nRestores a command to its original state. Effectively removes the stub and all\nspecial `::` commands from the bash environment.\n\n#### ::reset\n```bash\nstub 'ps'\nps\nps aux # Call count is now 2, last call arguments are now `aux`\nps::reset # Call count is no 0, last call arguments are now empty\n```\n\nResets all internal call counts and argument lists associated with a stub.\n\n#### ::returns `\u003coutput\u003e`\n\n- `\u003coutput\u003e` - Output to be piped to `stdout` by the command\n\n```bash\nstub 'yes'\n# `yes` will now print `no way` every time it is called\nyes::returns 'no way'\n```\nSets a stub to output the given string to `stdout` and return a `0` status code.\n\n#### ::errors `\u003coutput\u003e` `[code=1]`\n\n- `\u003coutput\u003e` - Output to pipe to `stderr` when the command is run\n- `[code=1]` - Optional status code for the stub (defaults to `1`)\n\n```bash\nstub 'mkdir'\nmkdir::errors 'Refusing to make directories' 69\n```\n\nSets a stub to error by printing the given string to `stderr` and returning the\ngiven status code.\n\n\n#### ::exec `\u003cexec-command\u003e`\n- `\u003cexec-command\u003e` - Command or function to execute when the stub is called\n\n```bash\nstub 'pwd'\npwd_stub() {\n  echo '/stfu/no/ob'\n}\npwd::exec pwd_stub\n```\n\nSets a stub to execute the given command or function when it is called.\n\n#### ::on_call `\u003ccall-number\u003e` `\u003cexec-command\u003e`\n\n- `\u003ccall-number\u003e` - The number of the call to override\n- `\u003cexec-command\u003e` - Command to execute on the given call number\n\n```bash\nstub::returns 'ls' 'default'\nls::on_call 2 'echo two'\nls::on_call 4 'echo four'\n\nls # Prints 'default'\nls # Prints 'two'\nls # Prints 'default'\nls # Prints 'four'\n```\n\nOverride the default behavior for the stub for the given call number by having\nit run the given command instead.\n\n#### ::called_with `\u003carg1\u003e` `[arg2 ...]`\n- `\u003carg1\u003e [arg2 ...]` - Arguments with which the stub should have been called.\n\n```bash\nstub 'cd'\ncd /awesome\ncd::called_with '/awesome' # Returns 0\ncd::called_with '/aewsom3' # Returns 1\n```\n\nAsserts that a stub was called with the given arguments the last time it was\nexecuted.\n\n#### ::called `[number=1]`\n- `[number=1]` - Number of times to assert that the stub was called (defaults to\n  1)\n\n```bash\nstub 'grep'\nls -la / | grep 'neat'\ngrep::called    # Returns 0\ngrep::called 14 # Returns 1\n```\n\nAsserts that a stub was called the given number of times.\n\n#### ::not_called\n\n```bash\nstub 'cwd'\ncwd\nstub 'mc'\nmc::not_called  # Return 0\ncwd::not_called # Returns 1\n```\n\nAsserts that the stub was not called.\n\n#### ::called_once\n\n```bash\nstub 'cp'\ncp::called_once # Returns 1\ncp a.txt b.txt\ncp::called_once # Returns 0\ncp b.txt c.txt\ncp::called_once # Returns 1\n```\n\nAsserts that the stub was called exactly once.\n\n#### ::called_twice\n\n```bash\nstub 'mv'\nmv alpha omega\nmv::called_twice # Return 1\nmv omega upsilon\nmv::called_twice # Return 0\nmv upsilon phi\nmv::called_twice # Returns 1\n```\n\nAsserts that the stub was called exactly twice.\n\n#### ::called_thrice\n\n```bash\nstub 'ls'\nls::called_thrice # Returns 1\nls \u0026\u0026 ls \u0026\u0026 ls\nls::called_thrice # Returns 0\nls\nls::called_thrice # Return 1\n```\n\nAsserts that the stub was called exactly three times.\n\n## License\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frunnable%2Fshtub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frunnable%2Fshtub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frunnable%2Fshtub/lists"}