Tiny mock library for Jasmine
mock = require'jasmine-mocks'mock;mockInstance = mockClazz;// mockInstance is now an object that contains every function on Clazz's// prototype as a spywhen = require'jasmine-mocks'when;whenmockInstancefooisCalledWith"bar"thenReturn"baz";// any call to the foo spy with the string "bar" will return the string "baz"argThat = require'jasmine-mocks'argThat;var hasLength n =returnreturn arrlength === n;;whenmockInstancefooisCalledWithargThathasLength3thenReturn"baz";// any call to foo with an array of length 3 will return the string "baz"
In my experience with Jasmine, when you deal with one class having a dependency on another you end up doing one of two things, using "real objects" or "class skeletons".
Class A has a dependency on Class B. Let's pretend we have been trying to keep classes separate and are passing an instance of B in whenever we create an instance of A.
This code looks like:
varthisb = b;;describe'A'it'calls the appropriate method on B'var b = ;spyOnb 'execute';bdoSomething;expectbexecutetoHaveBeenCalled;;;
When B has more than one function (perhaps it is a domain model), this leads to complicated setups requiring where stubbed models are created, spying on multiple methods, and sometimes describing different behavior for each one.
Sometimes some methods of B are not overridden, involving tests that do not properly stub out their dependencies. This is even worse!
A different approach is to create a 'class skeleton' of B and pass that into A. This looks like:
varthisb = b;;describe'A'it'calls the appropriate method on B'var b =execute: jasminecreateSpy'execute'bdoSomething;expectbexecutetoHaveBeenCalled;;;
This is a little better but still prone to problems. If the definition of B changes all of our tests must be updated, and it requires a lot of duplicate boilerplate between tests.
The canonical way to treat different calls to spies differently in Jasmine is to write something like the following:
spyOnb 'getData'andCallFakeif userId === 'joe'return name: 'Joe' id: 'ABQ-394'if userId === 'jane'return name: 'Jane' id: 'POW-889'throw 'Invalid User';;
I have found that this adds a little too much boilerplate and code to setup, while not allowing me to easily have tests fail if spies are passed incorrect values. (I would rather write define what arguments will be passed to my spies in setup than assert that the spy got called with the correct arguments.)