All Projects → codehearts → Shpy

codehearts / Shpy

Licence: mit
🕵️‍♀️ POSIX compliant spies and stubs for shell unit testing

Programming Languages

shell
77523 projects

Projects that are alternatives of or similar to Shpy

Ok.sh
A Bourne shell GitHub API client library focused on interfacing with shell scripts
Stars: ✭ 365 (+7200%)
Mutual labels:  posix
E
A dead simple editor
Stars: ✭ 440 (+8700%)
Mutual labels:  posix
Tinydir
Lightweight, portable and easy to integrate C directory and file reader
Stars: ✭ 575 (+11400%)
Mutual labels:  posix
Mrsh
A minimal POSIX shell
Stars: ✭ 381 (+7520%)
Mutual labels:  posix
Kirc
A tiny IRC client written in POSIX C99.
Stars: ✭ 416 (+8220%)
Mutual labels:  posix
Mons
POSIX Shell script to quickly manage monitors on X
Stars: ✭ 457 (+9040%)
Mutual labels:  posix
Goofys
a high-performance, POSIX-ish Amazon S3 file system written in Go
Stars: ✭ 3,932 (+78540%)
Mutual labels:  posix
Lizardfs
LizardFS is an Open Source Distributed File System licensed under GPLv3.
Stars: ✭ 793 (+15760%)
Mutual labels:  posix
Coursebook
Open Source Introductory Systems Programming Textbook for the University of Illinois
Stars: ✭ 437 (+8640%)
Mutual labels:  posix
Acme.sh
A pure Unix shell script implementing ACME client protocol
Stars: ✭ 24,723 (+494360%)
Mutual labels:  posix
Filer
Node-like file system for browsers
Stars: ✭ 389 (+7680%)
Mutual labels:  posix
Plibsys
Highly portable C system library: threads and synchronization primitives, sockets (TCP, UDP, SCTP), IPv4 and IPv6, IPC, hash functions (MD5, SHA-1, SHA-2, SHA-3, GOST), binary trees (RB, AVL) and more. Native code performance.
Stars: ✭ 402 (+7940%)
Mutual labels:  posix
Cpp Cheat
MOVING TO: https://github.com/cirosantilli/linux-kernel-module-cheat#userland-content SEE README. C, C++, POSIX and Linux system programming minimal examples. Asserts used wherever possible. Hello worlds for cool third party libraries and build systems. Cheatsheets, tutorials and mini-projects.
Stars: ✭ 474 (+9380%)
Mutual labels:  posix
Sh
A shell parser, formatter, and interpreter with bash support; includes shfmt
Stars: ✭ 4,343 (+86760%)
Mutual labels:  posix
Embox
Modular and configurable OS for embedded applications
Stars: ✭ 576 (+11420%)
Mutual labels:  posix
Ecominit
eComInit is a free init system and service manager designed to scale from lightweight desktops to web-scale cloud deployments. It aims to offer feature-parity with systemd but with a modular, portable architecture compliant with software engineering best-practice.
Stars: ✭ 352 (+6940%)
Mutual labels:  posix
Cobra
A Commander for modern Go CLI interactions
Stars: ✭ 24,437 (+488640%)
Mutual labels:  posix
Pfetch
🐧 A pretty system information tool written in POSIX sh.
Stars: ✭ 816 (+16220%)
Mutual labels:  posix
Modernish
Modernish is a library for writing robust, portable, readable, and powerful programs for POSIX-based shells and utilities.
Stars: ✭ 586 (+11620%)
Mutual labels:  posix
Kapow
Kapow! If you can script it, you can HTTP it.
Stars: ✭ 526 (+10420%)
Mutual labels:  posix

shpy

Build Status Coverage MIT License

POSIX compliant* spies and stubs for shell unit testing

ash bash dash mksh zsh
Ash Build Status Bash Build Status Dash Build Status Mksh Build Status Zsh Build Status

Features at a glance:

  • Create spies for any command or function in the shell environment
  • Stub the stdout, stderr, and return value of spies
  • See the call count and check arguments passed to spies
  • Integrates with the shunit2 testing framework

Table of Contents

Why Unit Test Shell Scripts?

Like other scripting languages, shell scripts can become complex and difficult to maintain over time. Unit tests help to avoid regressions and verify the correctness of functionality, but where do spies come in?

Spies are useful for limiting the dependencies and scope of a test. Code that utilizes system binaries or shell functions can be tested without running the underlying implementations, allowing tests to focus solely on the system under test. To see this in action, see examples/renamer

The benefits of spies are even greater when testing code that relies on a network. For an example of using spies to stub curl and make unit tests completely offline, see examples/coverfetch

Docker Image

Shpy is available as shpy/shpy on Docker Hub. The latest master node is published as shpy/shpy:latest, while tagged releases are available as shpy/shpy:1.0.0. To use kcov, append -kcov to the tag or use the kcov tag for the latest master node

To use the shpy image, mount your code into /app and specify the command you want to run. When using kcov, you can also mount /coverage and output your coverage reports to that directory

docker --rm -v$PWD:/app:ro shpy/shpy:1.0.0 zsh /app/tests/run_my_tests.sh
#           ^-your project                 ^--------your command---------

The following scripts and binaries are provided by this image

Name Type Location
shpy script /shpy/shpy
shpy-shunit2 script /shpy/shpy-shunit2
shunit2 script /usr/local/bin/shunit2
ash binary /bin/sh
bash binary /bin/bash
checkbashisms binary /usr/bin/checkbashisms
dash binary /usr/bin/dash
mksh binary /bin/mksh
shellcheck binary /usr/local/bin/shellcheck
zsh binary /bin/zsh

Usage

Let's try out shpy! If you don't want to install shpy locally you can run the official Docker image like so:

docker run -it --rm shpy/shpy:1.0.0

To use shpy, the SHPY_PATH environment variable must be set as the path to shpy and the shpy script must be sourced. If you're using the Docker image, SHPY_PATH is already set and shpy is located at /shpy/shpy

SHPY_PATH=path/to/shpy
. path/to/shpy

Let's create a spy for the touch command and call it!

createSpy touch
touch my-new-file
ls my-new-file # No such file or directory, touch wasn't actually called

The call to touch was stubbed out with a test dummy in place of the actual implementation. Spies record data about the calls made to them, allowing you to check the call count or call args

getSpyCallCount touch # 1
wasSpyCalledWith touch my-new-file # true
wasSpyCalledWith touch my-old-file # false
getArgsForCall touch 1 # my-new-file

Spies can also simulate successful or unsuccessful calls, like so:

createSpy -o 'call me once, shame on you' -e '' -r 0 \
          -e 'call me twice, shame on me' -o '' -r 1 touch
touch my-new-file # outputs "call me once, shame on you" to stdout, returns true
touch my-new-file # outputs "call me twice, shame on me" to stderr, returns false

When you're done playing with shpy, it's only polite to clean up after yourself

cleanupSpies
touch my-new-file
ls my-new-file # my-new-file, touch was actually called!

Your shell environment is back to normal, and you've got a new tool at your disposal! 🎓

Contributing

If you'd like to help with shpy's development, or just gain a better understanding of the internals, check out the contributor guidelines

API Reference

To use shpy in your tests, set SHPY_PATH to the location of shpy and source the script:

SHPY_PATH=path/to/shpy
export SHPY_PATH

. path/to/shpy

When using the Docker image, SHPY_PATH is preset as /shpy/shpy for convenience

The SHPY_VERSION environment variable is provided to get the current shpy version

A summary of functions:

Function Description
createSpy name Create a new spy, or reset an existing spy
createSpy -r status name Sets the status code returned when the spy is invoked
Can be passed multiple times to set a return value sequence
Once the sequence finishes, the last value is always returned
createSpy -o output name Sets output sent to stdout when the spy is invoked
Can be passed multiple times to set an output sequence
Once the sequence finishes, the last value is always output
When used with -e, standard out is written to first
createSpy -e output name Sets output sent to stderr when the spy is invoked
Can be passed multiple times to set an error output sequence
Once the sequence finishes, the last value is always output
When used with -o, standard out is written to first
createStub name Alias for createSpy
getSpyCallCount name Outputs the number of invocations of a spy
wasSpyCalledWith name [arg ...] Returns 0 if the current spy call under examination has the given args
getArgsForCall name call Prints the arguments from a call to a spy (first call is 1)
Single-word arguments are always listed without quotes
Multi-word arguments are always listed with double-quotes
examineNextSpyCall name Examine the next spy invocation when calling wasSpyCalledWith
This causes wasSpyCalledWith to verify the second invocation, etc
cleanupSpies Clean up any metadata on disk or in the environment for a spy

shunit2 Integration

To use shpy asserts in your shunit2 tests, you must also source the shpy-shunit2 script:

. path/to/shpy
. path/to/shpy-shunit2

A summary of asserts:

Function Description
assertCallCount [message] spy count Assert the number of times the spy was invoked
assertCalledWith spy [arg ...] Assert the arguments for the first invocation of the spy
Subsequent calls will assert for the second invocation, etc
assertCalledWith_ message spy [arg ...] Same as assertCalledWith, with a specific assertion message
assertCalledOnceWith spy [arg ...] Assert the spy was called once and given the specified arguments
assertCalledOnceWith_ message spy [arg ...] Same as assertCalledOnceWith, with a specific assertion message
assertNeverCalled [message] spy Assert the spy was never invoked

Use the tearDown hook provided by shunit2 to remove all spies after each test

tearDown() {
  cleanupSpies
}

A Word On Shell Portability

shpy relies on portable but more modern shell features, such as the local keyword. To be clear, shpy does not use any Bashisms

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].