test in action week 3
TRANSCRIPT
![Page 1: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/1.jpg)
Test in Action – Week 3Stub / Mock
Hubert Chan
![Page 2: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/2.jpg)
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.
![Page 3: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/3.jpg)
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);
![Page 4: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/4.jpg)
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
![Page 5: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/5.jpg)
Substitute Dependency
• Using Fake– Fake is anything not real
• Fake techniques– Mock object– Stub object– Fake object
![Page 6: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/6.jpg)
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
![Page 7: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/7.jpg)
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
![Page 8: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/8.jpg)
Example: AlertSystem
![Page 9: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/9.jpg)
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
![Page 10: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/10.jpg)
AlertSystem Test
• Dependency Analysis– Collaboration Classes
• NotifierInterface• NotifierTargetProviderInterface
• Tests should– Only focus on AlertSystem– Fake the dependency
![Page 11: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/11.jpg)
Make a Fake?
• FileNotifyTargetProviderclass 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; }}
![Page 12: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/12.jpg)
Make a Stub?
• StubNotifierclass StubNotifier implements NotifierInterface { public function notify($target, $content) { }}
![Page 13: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/13.jpg)
Example: Test Code
• Test Codeclass 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!!'); }}
![Page 14: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/14.jpg)
Manual Fake or Stub
• Pros– Fake or stub can be used as library– Shared implementation
• Cons– Different scenarios need different implementation– Testing class explosion
![Page 15: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/15.jpg)
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
![Page 16: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/16.jpg)
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?
![Page 17: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/17.jpg)
Mock objects
• Mock Objects– Mocks are objects pre-programmed with expectations, which form a specification of the calls they are expected to receive.
![Page 18: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/18.jpg)
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!!'); }
![Page 19: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/19.jpg)
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
![Page 20: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/20.jpg)
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!!'); }
![Page 21: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/21.jpg)
Using PHPUnit Stub
• Return one of the argumentspublic 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'));}
![Page 22: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/22.jpg)
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'));}
![Page 23: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/23.jpg)
PHPUnit Stub
• Throw Exceptionpublic 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();}
![Page 24: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/24.jpg)
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
![Page 25: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/25.jpg)
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
![Page 26: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/26.jpg)
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
![Page 27: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/27.jpg)
AlertSystem again
• AlertSystem (Hard to Test)– Concrete classes– Make Your Own Dependenciesclass 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); } }}
![Page 28: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/28.jpg)
AlertSystem constructor injection
• Constructor Injectionclass 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); } }}
![Page 29: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/29.jpg)
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
![Page 30: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/30.jpg)
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.
![Page 31: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/31.jpg)
Violation of the Law
• How do you test for?– Mock for mock object, for another mock object– Like Looking for a Needle in the Haystackclass Monitor { SparkPlug sparkPlug; Monitor(Context context) { this.sparkPlug = context. getCar().getEngine(). getPiston().getSparkPlug(); }}
![Page 32: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/32.jpg)
Law of Demeter - Explicit
• Explicit– We should not need to know the details of
collaboratorsclass Mechanic { Engine engine; Mechanic(Engine engine) { this.engine = engine; } }
![Page 33: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/33.jpg)
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
![Page 34: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/34.jpg)
Conclusion
• Writing good unit tests is hard• Good OO design brings good testability• Using stub/mock from mocking framework
![Page 35: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/35.jpg)
Q & A
![Page 36: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/36.jpg)
PHPUnit Log File
• Junit Format<testsuites> <testsuite name="AlertSystemTest" file="/usr/home/hubert/own/work/trend/process/CI/php/tests/AlertSystemTest.php" tests="2" assertions="0" failures="1" errors="0" time="0.031119"> <testcase name="test_sendNotify_FakeNStub_NoException" class="AlertSystemTest" file="/usr/home/hubert/own/work/trend/process/CI/php/tests/AlertSystemTest.php" line="8" assertions="0" time="0.006881"/> </testsuite></testsuites>
![Page 37: Test in action week 3](https://reader036.vdocuments.us/reader036/viewer/2022062405/554f3d9cb4c90572088b50d5/html5/thumbnails/37.jpg)
PHPUnit Coverage
• PHPUnit Coverage– Need Xdebug– HTML format