This document discusses low-cost techniques for test doubles in test-driven development (TDD) that are light, fast, easy to read and maintain. It recommends using real objects when possible instead of doubles to reduce dependencies on mocking frameworks. Specific techniques discussed include self-shunting where the test case itself acts as the double, and anonymous classes defined inline to create doubles without a framework.
3. The point of
going low-cost
Test should be
• Light
• Fast
• Easy to read
• Easy to maintain
4. The point of
going low-cost
Reduce dependency on mocking
frameworks
Strictly speaking, mocking frameworks
break isolation principle
Use the things you have at hand
7. Don’t double Use real objects when:
• They have no behavior or it is very
simple
• They have no side effects
• They are immutable
• They have no dependencies (or depend
on similar objects)
8. Don’t double
$request = new GetObjectsRequest (‘param1’, ‘param2’);
$serviceUnderTest = new Service();
$result = $serviceUnderTest->execute($request);
$this->assertEquals(‘expected’, $result);
9. Self-shunt Use the TestCase as double (WTF!)
Great for simple doubles
Great for early stages of TDD
10. Self-shunt You will need an interface to double
Make the test double implement interface
You can implement spies
11. Self-shunt stub
Class ServiceTest extends TestCase implements GetObjectsRequestInterface
{
public function testSomething()
{
$serviceUnderTest = new Service();
$result = $serviceUnderTest->execute($this);
$this->assertEquals(‘expected’, $result);
}
public function param1()
{
return ‘param1’;
}
public function param2()
{
return ‘param2’;
}
}
16. Anonymous class
class ServiceTest extends TestCase
{
public function testSomething()
{
$collaborator = new class ()
implements CollaboratorInterface
{
private $calls = 0;
public function doThatThing(): string
{
$this->calls++;
return ‘something’;
}
public function calls()
{
return $this->calls;
}
};
$serviceUnderTest = new Service($collaborator);
$result = $serviceUnderTest->execute();
$this->assertEquals(‘expected’, $result);
$this->assertEquals(1, $collaborator->calls());
}
}