Test doubles

Tien Vo Xuan

Drupal Developer

Go1

What's in this topic

  • What is test doubles?
  • 5 kinds of test doubles by examples
  • How to recognize them?

Test double

or Mock

or Stunt Double (in movies)


Just one thing

When we are writing a test in which we cannot (or chose not to) use a real depended-on component (DOC), we can replace it with a Test Double. The Test Double doesn't have to behave exactly like the real DOC; it merely has to provide the same API as the real one so that the system-under-test (SUT) thinks it is the real one!

Gerard Meszaros

application

Kinds of double

  • Dummy
  • Fake
  • Stubs
  • Spies
  • Mocks

Kinds of double

Dummy

objects are passed around but never actually used. Usually they are just used to fill parameter lists.

Dummy

class CarController {
function getReadyToGo(EngineInterface $engine, GearboxInterface $gearbox, ElectronicsInterface $electronics, LightsInterface $lights) {
        $engine->start();
        $gearbox->shift('N');
        $electronics->turnOn($lights);
        return true;
    }
}

Dummy

$light = $this->getMock('LightInterface');


Dummy

  • It does not matter how many times a method is called
  • It return null

fake

  • It does not matter how many times a method is called
  • It does not return null

Fake

$statusPanel = $this->getMock('StatusPanel')
->expects($this->any())
->method('getCurrentSpeed')
->will($this->returnCallback(
    function() use ($distance, $time) {
        return $distance / $time;
    }
));


Stub

stub

function goForward(Electronics $electronics, StatusPanel $statusPanel = null) {
    $statusPanel = $statusPanel ? : new StatusPanel();
    if($statusPanel->engineIsRunning() && $statusPanel->thereIsEnoughFuel())
        $electronics->accelerate();
}

stub

$stubStatusPanel = $this ->getMock( 'StatusPanel' ) ->expects( $this ->any()) ->method( 'thereIsEnoughFuel' ) ->will( $this ->returnValue(TRUE)); $stubStatusPanel->expects($this->any()) ->method('engineIsRunning') ->will( $this ->returnValue(TRUE));

Stub

  • It does not matter which arguments are provided when one of its methods is called.
  • It does not matter how many times a method is called.
  • It return a fixed value.

spy

  • Keep track of method being called.
  • To later examine.

Spy

$currentDirection = null;
$carSpy = $this->getMock('CarController');
$carSpy->expects($this->any())
->method('turnLeft')
->will($this->returnCallback(
    function() use(&$currentDirection) {
        $currentDirection = 'left';
    }
));
// turn right ...


Spy

$remote = new Remote($carSpy);
$remote->turnRight();
$remote->turnLeft();
// ...
$this->assertEquals('right', $currentDirection);


mock

  • Just like Spy
  • Also: Make sure that the methods were called the expected number of times, in a particular order, with the correct arguments.

mock

$carMock = $this->getMock('CarController');
$carMock->expects($this->at(0))
->method('turnLeft')
->with(30);
$carMock->expects($this->at(1))
->method('turnLeft')
->with(20);
$carMock->expects($this->at(0))
->method('turnRight')
->with(15);


Mock

$remote = new Remote($carMock);
$remote->turnLeft(30);
$remote->turnLeft(20);
$remote->turnRight(15);


mock

The mock is not so interested in the return values of functions. It's more interested in what function were called, with what arguments, when, and how often.


Recognize

  • Dummy: neither expects() or method() or with() or will()
  • Stub:  expects($this->any()) and will($this->returnValue('static value'))
  • Fake: expects($this->any()) and will($this->returnCallBack('a callback'));
  • Spy: expects($this->any()) and will($this->returnCallBack('use and assign value to global variables'));
  • Mock: expects(either at or once or exactly or atLeastOnce or never) and with('arguments') and without will()

references

Q&A

Test double

By Tiến Võ Xuân

Test double

  • 1,142