An extensible dynamic unit test generator.
npm install saul!Build Status 

saul gives you a custom DSL that will help you write test framework agnostic unit tests for your javascript functions.
A simple example might look like:
``js
// @t "should call saul when the threat is imminent" shouldCallSaul('imminent') ~equals true
// @t "should not call saul when threat is not imminent" shouldCallSaul('nodanger') ~equals false
function shouldCallSaul(threatLevel) {
if (threatLevel === 'imminent') {
return true;
}
return false;
}
`
- Avoid writing unnecessary boilerplate code for trivial tests
- Quickly test functionality with // @t annotations in your code
- Have your tests co-located to the functionality it tests
- Self-document your functionality with a custom DSL
`sh`
yarn add --dev saul
example:
`js`
{
"fileGlob": "src/*/.js", // files that contain the saul comments
"customEnginesDir": "./src/custom-saul-engines" // optional: dir where you will put custom engine .js files
}
#### Mocha/Jasmine
If you have some mocha tests already, your npm test would look like: mocha src/**/.js. Simple add saul's bin (node_modules/.bin/saul) right at the end:
`sh`
mocha lib/*.test.js" node_modules/.bin/saul
#### Jest
Since jest requires a regex pattern for test files, you will have to create a file with a single file with a require, that will be matched by your jest regexPattern.
Example:
`js`
require('saul'); // will run all saul tests here
Any transformation that you apply to your tests will be inherited by saul when you run your tests. If you're running babel, this will include anything that you define in your local .babelrc.
For an instance, if you want to feed babel-transformed files to mocha, you will invoke mocha with mocha --compilers js:babel-register. You can simply add saul to the end of the command. (mocha --compilers js:babel-register node_modules/.bin/saul) - and things will Just Work™.
Example:
`js`
// @t "appends foo" appendFoo('bar') ~expect expect(result).to.contain('foo');
// @t "has no fizz" appendFoo('bar') ~expect expect(result).to.not.contain('fizz');
export function appendFoo (someString) {
return someString + 'foo';
}
With spy support:
Calling spy(name: string), will create a sinon spy. You can assert on any of it's methods/properties like this:
`js`
// @t "calls only once" testEvalSpy(spy('mySpy')) ~expect spy('mySpy').calledOnce
// @t "calls with obj" testEvalSpy(spy('mySpy2'), 'foo') ~expect spy('mySpy2').calledWith('foo')
export function testEvalSpy (fn, str) {
fn('foo', str);
}
`js
// @t "should render Date" Date({dateString: '1970-03-11'}) ~matches-snapshot
export function Date(props) {
return {props.dateString}
}
// @t "returns all months" getAllMonths() ~matches-snapshot
export function getAllMonths() {
return CONSTANTS.ALL_MONTHS.join('_');
}
`
js
// @t "can sum" sum(1, 2) ~equals 3
export function sum(numOne, numTwo) {
return numOne + numTwo;
}// @t "testEqualsAsync" testEqualsAsync() ~equals 'foo'
export function testEqualsAsync() {
return new Promise((resolve, reject) => {
resolve('foo');
});
}
`$3
Checks whether the output contains the expected value.Example:
`js
// @t "can concat" concatanate('string1', 'something els') ~contains 'string1'
export function concatanate (a, b) {
return a + b;
}
`$3
Checks whether the expected value is not equal to the actual value. (Opposite of equals)
`js
// @t "can sum" sum(1, 2) ~is-not 4
export function sum(numOne, numTwo) {
return numOne + numTwo;
}
`$3
Checks whether the invokation would throw.
`js
// @t "throws on null engine" executeTest({engine: null}) ~throws Error
export executeTest(options) {
options.engine('foobar');
}
`And more! See: extending saul.
Extending
saul Then engines are the "comparator" in the tests.
`js
// @t "throws on null engine" executeTest({engine: null}) ~throws Error
| | └ expected value
| |
| └ comparator
|
└ actutal value
`They are handled by the file of that name in
src/engines/. (Example: src/engines/throws.js)The "engines", are responsible for generating the tests. So, as long as you build a custom engine - it can pretty much test anything.
The default engines can do a few cool things out of the box. (check the
src/engines/ directory). You can always write your own engines and put them in your customEnginesDir defined in .saulrc.Examples
Just look through this repo for
// @t annotated tests. saul is tested with saul! :rocket:Contributions
Please! Here are som TODOs that need being done.
- [ ] More engines! (If you would like to contribute an engine, please take a look at the engine files at
src/engines`)