part 2
objects that mimic real behavior
xUnit Test Patterns
Test Double
|
|
Dummy
|
|
Spy
|
|
Mock
\
\
\
\
Fake
|
|
Stub
objects that do nothing
and return as close to "nothing"
0, "", null, undefined, {}, []
fill functions arguments, not needed in tests
// 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);
});
objects that do nothing
and return something useful for a use-case scenario
guide the test through a specific pathway
// 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] });
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
// 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();
});
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
multiple stubs, only one mock
per test
objects that simulate a real object
faking integration with 3rd party objects, external interfaces, libraries
tend to be difficult to maintain
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);
});
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