Test shell scripts while mocking specific commands
npm install jest-shell-matchers> Test shell scripts while mocking specific commands
Run shell scripts and make assertions about the exit code, stdout, stderr, and termination signal that are generated. It uses the spawn-with-mocks library, so mocks can be written for specific shell commands.
 
The library exposes asynchronous matchers, so it requires Jest 23 or higher (to run synchronous tests, use spawn-with-mocks directly). Mocks are created by writing temporary files to disk, so they do not work if fs.writeFileSync is being mocked.
Initialization
``javascript
const shellMatchers = require('jest-shell-matchers')
beforeAll(() => {
// calling this will add the matchers
// by calling expect.extend
shellMatchers()
})
`
Example Without Mocks
`javascript
it('should test the output from a spawned process', async () => {
// this input will be executed by child_process.spawn
const input = ['sh', ['./hello-world.sh']]
const expectedOutput = {
code: 0,
signal: '',
stdout: 'Hello World\n',
stderr: '',
}
// the matcher is asynchronous, so it must be awaited
await expect(input).toHaveMatchingSpawnOutput(expectedOutput)
})
`
Example With Mocks
Mocks are created by spawn-with-mocks, which documents the mocking API. In this example, we mock the date and mkdir commands:
`javascript
const fs = require('fs')
it('should mock the date and mkdir commands', async () => {
fs.writeFileSync(
'./mkdir.sh',
// this example script creates a directory
// that is named for the current date
#!/bin/sh
DIR_NAME=$(date +'%m-%d-%Y')
mkdir $DIR_NAME)
// Mocking the output
// for the date command
const date = () => {
return {
code: 0,
stdout: '01-06-2019',
stderr: ''
}
}
// Testing the input to mkdir,
// and mocking the output
const mkdir = jest.fn(dir => {
expect(dir).toBe('01-06-2019')
return {
code: 0,
stdout: '',
stderr: ''
}
})
const mocks = { date, mkdir }
const input = ['sh', ['./mkdir.sh'], { mocks }]
await expect(input).toHaveMatchingSpawnOutput(0)
expect(mocks.mkdir).toHaveBeenCalledTimes(1)
fs.unlinkSync('./mkdir.sh')
})
`
Mocks can also return a Number or String to shorten the code:
`javascript
// The string is shorthand for stdout;
// stderr will be '' and the exit code will be 0
const date = () => '01-06-2019'
// The number is shorthand for the exit code
// stdout and stderr will be ''
const mkdir = dir => 0
`
- To use the matchers, call expect with the input for spawn-with-mocks#spawn, which the matchers run internally. It can execute a script, create mocks, set enviroment variables, etc. When passing args or options, the input must be wrapped with an array:
`javascript
await expect('ls')
.toHaveMatchingSpawnOutput(/.../)
await expect(['sh', ['./test.sh'], { mocks }])
.toHaveMatchingSpawnOutput(/.../)
`
- The expected value can be a Number, String, RegExp, or Object.
`javascript
const input = ['sh', ['./test.sh']]
await expect(input)
// Number: test the exit code
.toHaveMatchingSpawnOutput(0)
await expect(input)
// String: test the stdout for an exact match
.toHaveMatchingSpawnOutput('Hello World')
await expect(input)
// RegExp: test the stdout
.toHaveMatchingSpawnOutput(/^Hello/)
await expect(input)
// Object: the values can be Numbers, Strings, or RegExps
.toHaveMatchingSpawnOutput({
// The exit code
code: 0,
// The signal that terminated the proces
// for example, 'SIGTERM' or 'SIGKILL'
signal: '',
// The stdout from the process
stdout: /^Hello/,
// The stderr from the process
stderr: ''
})
``
MIT