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