2. Back to Basics
• Wikipedia said unit testing is
– A software verification and validation method in
which a programmer tests if individual units of
source code are fit for use.
3. Dependency in the Test
• System Under Test (SUT) Dependency
– SUT need another module to perform its task
• Handler need a $dbmodel to perform query
• $handler = new Handler($dbmodel);
4. Real Object is Hard to Test
• Real object
– Supplies non-deterministic results
• (e.g. the current temperature)
– Difficult to create or reproduce (e.g. network fail)
– Slow (e.g. database)
– Does not yet exist or may change behavior
5. Substitute Dependency
• Using Fake
– Fake is anything not real
• Fake techniques
– Mock object
– Stub object
– Fake object
6. Stub Objects
• Stub Objects
– Stubs provide canned answers to calls made
during the test
– Usually not responding at all to anything outside
what's programmed in for the test
7. Fake Objects
• Fake Objects
– Fake objects have working implementations
– Usually take some shortcut, which makes them
not suitable for production.
– Example
• An in-memory file system
• An in-memory registry manager
9. Example: AlertSystem
• Component Overview
– AlertSystem
• Send notification to the targets, such as e-mail or SMS
notifier.
– NotifierInterface instance
• Send actually notification to the target
– NotifierTargetProviderInterface instance
• Provide a target array to send notification
10. AlertSystem Test
• Dependency Analysis
– Collaboration Classes
• NotifierInterface
• NotifierTargetProviderInterface
• Tests should
– Only focus on AlertSystem
– Fake the dependency
11. Make a Fake?
• FileNotifyTargetProvider
class FileNotifyTargetProvider
implements NotifyTargetProviderInterface {
function __construct($filename) {
$this->_filename = $filename;
}
public function get_targets() {
$targets = array();
$handle = @fopen($this->_filename, "r");
while (($target = fgets($handle)) !== FALSE) {
$targets[] = $target;
}
return $targets;
}
}
12. Make a Stub?
• StubNotifier
class StubNotifier implements NotifierInterface {
public function notify($target, $content) {
}
}
13. Example: Test Code
• Test Code
class AlertSystemTest extends PHPUnit_Framework_TestCase {
public function test_sendNotify_FakeNStub_NoException() {
$target_file = __DIR__ . '/data/targets.txt';
$notifier = new StubNotifier();
$provider = new FileNotifyTargetProvider($target_file);
$alert_system = new AlertSystem(
$notifier,
$provider
);
$alert_system->send_notify('Alert!!');
}
}
14. Manual Fake or Stub
• Pros
– Fake or stub can be used as library
– Shared implementation
• Cons
– Different scenarios need different implementation
– Testing class explosion
15. Manual Stub or Fake
• Cons
– Need extra efforts/logics for behaviors
• Setting return value
• Setting thrown exception
– Hard to validate
• Calling sequence of functions
• Passing arguments for functions
• The method should be called
16. Test Doubles
• Using manual stub and mock
– Is $notifier->notify() be called?
– Does the target of $notifier->notify equal to
expect target?
– Does the content of $notifier->notify equal
to expect target?
17. Mock objects
• Mock Objects
– Mocks are objects pre-programmed with
expectations, which form a specification of the
calls they are expected to receive.
18. Mock Object Example
• Mock Object Example
public function test_sendNotify_Mock_NoException() {
$notify_content = 'fake_content';
$mock_notifier = $this->getMock('NotifierInterface');
$mock_notifier->expects($this->once())
->method('notify')
->with($this->anything(),
$this->equalTo($notify_content));
$alert_system = new AlertSystem(
$mock_notifier,
$stub_provider
);
$alert_system->send_notify('Alert!!');
}
19. Mock Object Example
• Mock Object Verification
– $notifier->notify is called only once
– $notifier->notify 1st parameter can be
anything
– $notifier->notify 2nd parameter should be
equal to $notify_content
20. Using PHPUnit Stub
• Return Value
public function test_sendNotify_Mock_NoException() {
$stub_provider = $this->getMock('NotifyTargetProviderInterface');
$targets = array('hubert');
$stub_provider->expects($this->any())
->method('get_targets')
->will($this->returnValue($targets));
$alert_system = new AlertSystem(
$mock_notifier,
$stub_provider
);
$alert_system->send_notify('Alert!!');
}
21. Using PHPUnit Stub
• Return one of the arguments
public function testReturnArgumentStub() {
// Create a stub for the SomeClass class.
$stub = $this->getMock('SomeClass');
// Configure the stub.
$stub->expects($this->any())
->method('doSomething')
->will($this->returnArgument(0));
// $stub->doSomething('foo') returns 'foo'
$this->assertEquals('foo', $stub->doSomething('foo'));
// $stub->doSomething('bar') returns 'bar'
$this->assertEquals('bar', $stub->doSomething('bar'));
}
22. Using PHPUnit Stub
• Return a value from a callback
– Useful for “out” parameter
public function testReturnCallbackStub() {
// Create a stub for the SomeClass class.
$stub = $this->getMock('SomeClass');
// Configure the stub.
$stub->expects($this->any())
->method('doSomething')
->will($this->returnCallback('str_rot13'));
// $stub->doSomething($argument) returns str_rot13($argument)
$this->assertEquals('fbzrguvat', $stub->doSomething('something'));
}
23. PHPUnit Stub
• Throw Exception
public function testThrowExceptionStub() {
// Create a stub for the SomeClass class.
$stub = $this->getMock('SomeClass');
// Configure the stub.
$stub->expects($this->any())
->method('doSomething')
->will($this->throwException(new Exception));
// $stub->doSomething() throws Exception
$stub->doSomething();
}
24. Misconception About Mocks
• Mocks are just Stubs
– Mock is behavior verification
• Is the function called?
• Is the parameter passed correctly?
– Stub is used for state verification
– References
• Mocks Aren’t Stubs
– http://martinfowler.com/articles/mocksArentStubs.html
25. Is it HARD to use stub/mock?
• Untestable code
– Make Your Own Dependencies
– Heavy Duty Constructors
– Depend on Concrete Classes
– Use Statics
– Using Singleton Everywhere
– Look for Everything You Need
26. Dependency Injection
• Without dependency injection
– Use “new” operator inside your class
– Make mock object injection difficult
• Dependency Injection
– Inject dependencies through injectors
– Injection method
• Constructor
• Setter
• Dependency Injection Framework
27. AlertSystem again
• AlertSystem (Hard to Test)
– Concrete classes
– Make Your Own Dependencies
class AlertSystem {
public function send_notify($content) {
$target_provider = new FileNotifyTargetProvider('data.txt');
$notifier = new EmailNotifier('user', 'pass', $port);
$notify_targets = $target_provider->get_targets();
foreach ($notify_targets as $target) {
$notifier->notify($target, $content);
}
}
}
28. AlertSystem constructor injection
• Constructor Injection
class AlertSystem {
protected $_notifier;
protected $_target_provider;
function __construct (
NotifierInterface $notifier,
NotifyTargetProviderInterface $provider
) {
$this->_notifier = $notifier;
$this->_target_provider = $provider;
}
public function send_notify($content) {
$notify_targets = $this->_target_provider->get_targets();
foreach ($notify_targets as $target) {
$this->_notifier->notify($target, $content);
}
}
}
29. Not only for testing
• Single Responsibility Principle
– Should alert system holds notifier and data
provider logic?
– Ex. Should the class read registry directly?
• Dependency Inversion Principle
• Open Close Principle
30. The Law of Demeter
• Definition
– Each unit should have only limited knowledge
about other units: only units "closely" related to
the current unit.
– Each unit should only talk to its friends; don't talk
to strangers.
– Only talk to your immediate friends.
31. Violation of the Law
• How do you test for?
– Mock for mock object, for another mock object
– Like Looking for a Needle in the Haystack
class Monitor {
SparkPlug sparkPlug;
Monitor(Context context) {
this.sparkPlug = context.
getCar().getEngine().
getPiston().getSparkPlug();
}
}
32. Law of Demeter - Explicit
• Explicit
– We should not need to know the details of
collaborators
class Mechanic {
Engine engine;
Mechanic(Engine engine) {
this.engine = engine;
}
}
33. Guide – Write Testable Code
• Bad smell for non-testable Code
– Constructor does real work
– Digging into collaborators
– Brittle Global State & Singletons
– Class Does Too Much
• References
– Guide – Write Testable Code
– http://misko.hevery.com/attachments/Guide-
Writing%20Testable%20Code.pdf
34. Conclusion
• Writing good unit tests is hard
• Good OO design brings good testability
• Using stub/mock from mocking framework