spy4js
Benefits
- Well tested
- Used in production of large projects
- Flow support (therefore autocompletion in some IDEs)
- Without dependecies nor boilerplate
- Helpful Debug-Messages
- Supports own "equals" implementation on objects
- A lot of useful operations on spys
Introduction
spy4js provides a stand-alone spy framework. It is decoupled by any dependecies and other assertion frameworks. Other than most test frameworks it uses a different - maybe you will need to get used to - test notation. It does not make assertion, which are expected to be fulfilled on runtime, but displays facts, that are considered to be fulfilled on runtime. And if this fact is not true, it will throw an exception. I consider the way of writing facts more regular because it fits more to the rest of the written code.
spy4js comes with the one interesting (es6 like) class Spy
. The spy instances are treated as class instances and come with a lot of useful features. See below for more.
Installation (TODO)
Interface
A spy instance can be initialized differently.
import {Spy} from 'spy4js';
const spy1 = new Spy();
const spy2 = new Spy('special spy for me');
const someObject = new Date(2017, 01, 15);
const spy3 = Spy.on(someObject, 'toJSON');
const someObject = new Date(2017, 01, 15);
const [spy4, spy5, spy6] = Spy.onMany(someObject, 'toJSON', 'toString', 'getDate');
You may apply additional behaviour to every spy. The valid operations here are:
configure
(some external librarys may use own "equals" implementations in an unexpected way)calls
(does make the spy call the provided functions sequentially)returns
(does make the spy return the provided params sequentially)throws
(does make the spy throw an error when called)transparent
(does make the spy call the original method of a mocked object)transparentAfter
(does make the spy call the original method of a mocked object after a certain amount of made calls)reset
(resets the registered calls which were already made)restore
(does make the spy restore the mocked object)
All those methods on a spy are designed in a builder pattern. So you may chain any of these configurations. But be aware that some behaviours override existing behaviours.
const spy = Spy.on(someObject, 'someMethod');
spy.configure({useOwnEquals: false});
spy.calls(func1, func2, func3);
someObject.someMethod(arg)
someObject.someMethod(arg1, arg2)
someObject.someMethod(arg)
someObject.someMethod(arg1, arg2, arg3)
spy.returns(value1, value2);
someObject.someMethod(arg)
someObject.someMethod(arg1, arg2)
someObject.someMethod(arg)
spy.throws('throw this');
someObject.someMethod(arg)
spy.calls(() => new Date()).transparentAfter(2);
someObject.someMethod(arg)
someObject.someMethod(arg1, arg2)
someObject.someMethod(arg)
spy.transparent();
spy.reset();
spy.restore();
Even as important are the facts, we want to display:
wasCalled
(does display that the spy was called a specifiable amount of times)wasNotCalled
(does display that the spy was never called)wasCalledWith
(does display that the spy was called at least once like with the provided params)wasNotCalledWith
(does display that the spy was never like with the provided params)
Those methods on a spy display facts. Facts have to be true, otherwise they will throw an Exception, which displays in a formatted debug message why the given fact was a lie. By writing those facts in your tests, a big refactoring loses its scare.
const spy = new Spy();
spy.wasNotCalled();
spy([1, 'test', {attr: [4]}]);
spy.wasCalled();
spy.wasCalled(1);
spy('with this text');
spy.wasCalled(2);
spy.wasCalledWith([1, 'test', {attr: [4]}]);
spy.wasNotCalledWith([1, 'test', {attr: [3]}]);
There is one static method that does restore all existing spies in all tests. This is extremly useful to clean up all still existing mocks and also a very comfortable to this automaticly after every test (like in an "afterEach").
restoreAll
(does restore every existing spy)
Spy.restoreAll();
And also sometimes it is necessary to have access to some of the call arguments with which the spy was called.
getCallArguments
(returns all call arguments for a specified call in an array)getFirstCallArgument
(same as getCallArguments, but returns only the first element of the array)
const spy = new Spy();
spy('string', 1);
spy([1, 2, 3]);
spy();
spy(null);
spy.getCallArguments()
spy.getFirstCallArgument()
spy.getCallArguments(1)
spy.getFirstCallArgument(1)
spy.getCallArguments(2)
spy.getFirstCallArgument(2)
spy.getCallArguments(3)
spy.getFirstCallArgument(3)
spy.getCallArguments(4)
spy.getFirstCallArgument(4)
The last method is showCallArguments
. It is mostly used internally to improve the debug messages, but can be while you are in a console.log-mania.
Method-Details (TODO)