Test Doubles
part 2
Test Double
objects that mimic real behavior
xUnit Test Patterns
Gerard Meszaros
Test Double
|
|
Dummy
|
|
Spy
|
|
Mock
\
\
\
\
Fake
|
|
Stub
Dummy
objects that do nothing
and return as close to "nothing"
0, "", null, undefined, {}, []
What?
fill functions arguments, not needed in tests
When?
// sut var obj = { prop: null, init: function( fn, val ) {
if ( !dep.isOk() ) return;
this.prop = val;
(typeof fn === "function") && fn();
dep.do( val );
}
};
// passing dummy data it("should init prop", function() {
obj.init(undefined, 1);
expect( obj.prop ).toEqual(1);
});
Stub
objects that do nothing
and return something useful for a use-case scenario
guide the test through a specific pathway
When?
// stubbing with Jasmine it("should init prop", function() {
spyOn(dep, 'isOk').and.returnValue(true);
obj.init(undefined, 1);
expect( obj.prop ).toEqual(1);
});
// stubbing with Sinon it("should init prop", function() {
sinon.stub(dep, 'isOk').returns(true);
obj.init(undefined, 1);
expect( obj.prop ).toEqual(1);
});
// "mocked" requests are in fact "stubs" $.mockjax({ url: 'url', responseText: [1, 2, 3] });
Spy
objects that do nothing,
return something useful for a use-case scenario
and record their actions
verify if methods got called, how many times, with what arguments, etc
When?
// sut var obj = { prop: null, init: function( fn, val ) {
if ( !dep.isOk() ) return;
this.prop = val;
(typeof fn === "function") && fn();
dep.do( val );
}
};
// spying with Jasmine
it("should call fn", function() {
var fnSpy = function() {};
spyOn(fnSpy).and.callThrough();
obj.init(fnSpy, 0); expect(fnSpy).toHaveBeenCalled();
});
// spying with Sinon
it("should call fn", function() {
var fnSpy = function() {};
sinon.spy(fnSpy);
obj.init(fnSpy, 0); expect(fnSpy.called).toBeTruthy();
});
Mock
objects that do nothing,
return something useful for a use-case scenario,
record their actions
and know what should happen
// sut var obj = { prop: null, init: function( fn, val ) {
if ( !dep.isOk() ) return;
this.prop = val;
(typeof fn === "function") && fn();
dep.do( val ); }
};
// mocking with Jasmine
it("should call do() with val", function() {
spyOn(dep, 'do');
obj.init(undefined, 1); expect(dep.do).toHaveBeenCalledWith(1);
});
// mocking with Sinon
it("should call do() with val", function() {
var mock = sinon.mock( dep );
mock.expects('do').withArgs(1);
obj.init(undefined, 1); mock.verify(); });
if you assert on them, they are mocks if not, if they have setup purpose, they are stubs
aahhhh... what?
multiple stubs, only one mock
per test
Best practice
Fake
objects that simulate a real object
faking integration with 3rd party objects, external interfaces, libraries
When?
tend to be difficult to maintain
!!!
TAke Away
use stubs for getters
use mocks/spies for setters
// stub it("should init prop", function() {
spyOn(dep, 'isOk').and.returnValue(true); obj.init(undefined, 1); expect( obj.prop ).toEqual(1);
});
// mock
it("should call do() with val", function() {
spyOn(dep, 'do'); obj.init(undefined, 1); expect(dep.do).toHaveBeenCalledWith(1);
});
Why Should I Care ?!
frameworks
Metallica
BDD
Understanding Mock Objects https://www.youtube.com/watch?v=fAb_OnooCsQ Mocks Aren't Stubs (Martin Fowler) http://goo.gl/nrRMpN Difference between faking, mocking, and stubbing http://goo.gl/31pijB JavaScript Mock Frameworks comparison http://goo.gl/w2etmo
Thank You
Testing part 2: Test Doubles
By Andrei Pfeiffer
Testing part 2: Test Doubles
Part of the testing terminology, this presentation focuses only on Stubs, Mocks, Spies, Fakes, etc.
- 874