33
Test in Action – Week 4 Good Tests Hubert Chan

Test in action week 4

Embed Size (px)

Citation preview

Page 1: Test in action   week 4

Test in Action – Week 4Good Tests

Hubert Chan

Page 2: Test in action   week 4

Test Lint

• Test Lint– Trustworthiness– Maintainability– Readability

• References– Test Lint (http://docs.typemock.com/lint/ )

Page 3: Test in action   week 4

Trustworthy Tests

• Trustworthy Tests– Always Asserts Something– Avoid unpredictable factor– Don’t depend on other tests– Avoid Logic in Tests

Page 4: Test in action   week 4

Always Asserts Something

• Always Asserts Something– Assertion means “Verification”– Unit Tests need to “Check Something”

• Exceptions– Check exception not thrown• Name “Login_Call_NoExceptionThrown”

– Specified Mock Object

Page 5: Test in action   week 4

Avoid Unpredictable Factor

• Avoid Unpredictable Factor– Unpredictable Factor• Random Number• Date Time

– Cause• Unreliable/Inconsistent Result• Hard to write Assertion statement

Page 6: Test in action   week 4

Avoid Unpredictable Factor

• Solution– Use Fake– Use hard-code values

$pseudoRandom = 1234;$this->assertEquals(1, sut.DoSomething(pseudoRandom));

$random = rand();$this->assertEquals(1, sut.DoSomething($random));

Page 7: Test in action   week 4

Don’t Depend on Other Tests

• Exampleclass LogAnalyzerDependTest extends PHPUnit_Framework_TestCase {

public function test_LogAnalyzerDepend_Construct_NoException() { $this->analyzer = new LogAnalyzerDepend(); $this->analyzer->initialize(); }

public function test_IsValid_InValidContent_ReturnFalse() { $this->test_LogAnalyzerDepend_Construct_NoException(); $this->assertFalse($this->analyzer->is_valid('abc')); }}

Page 8: Test in action   week 4

Don’t Depend on Other Tests

• Symptom– A test calls another tests– It requires other tests to create/delete objects– A test depends on system state set by other tests– Test order matters

Page 9: Test in action   week 4

Don’t Depend on Other Tests

• Why not?– Cannot provide explicit testing result– Implicit tests flow– The testA failed because callee testB failed

• Solution– Extract reused code to utility– For create/delete object, use “setUp” and

“tearDown” instead

Page 10: Test in action   week 4

Avoid Logic in Tests

• Test more than one thing– Number of Assertion > 1– Logics better not in tests• switch, if, or else statement• foreach, for, or while loops

Page 11: Test in action   week 4

Avoid Logic in Tests- Example

• Test Codepublic function test_ImplodeAndExplode_ValidContent_CorrectResult() { $instance = new MoreThanOne(); $test_array = array('1', '2', '3'); $test_string = '1,2,3';

for ($i = 0; $i < 2; $i++) { if ($i === 0) { // Test explode2 $result = $instance->explode2(',', $test_string); $this->assertEquals($test_array, $result); } elseif ($i === 1) { // Test implode2 $result = $instance->implode2(',', $test_array); $this->assertEquals($test_string, $result); } }}

Page 12: Test in action   week 4

Make Clean Tests

• Change your tests– Removing invalid tests– Renaming / Refactoring tests– Eliminating duplicate tests

Page 13: Test in action   week 4

Trustworthiness – Do it

• Do it– Testing only one thing– Keep safe green zone• No dependency to real database/network• Keep result consistent

– Assuring code coverage– Attitude• Add a unit test for newly tickets/trackers

Page 14: Test in action   week 4

Maintainable Tests

• Maintainable Tests– Test public function only– Don’t Repeat Yourself• Use Factory Function over Multiple Object Creation• Use setUp

– Using setUp in a maintainable manner– Avoid Over Specification in Tests– Avoid multiple asserts

Page 15: Test in action   week 4

Test Public Function Only

• Test Public Function Only– Design/Program by Contract• Private/Protected method might be changed

– Extract private/protected function to new class• Adopt Single Responsibility Principle (SRP)

Page 16: Test in action   week 4

Semantic Change

• Semantic Change is really a PAIN– API change may break all tests– Each test need to be changed

public function test_IsValid_InValidContent_ReturnFalse_New() { $analyzer = new LogAnalyzer(); $analyzer->initialize(); // new requirement $this->assertFalse($analyzer->is_valid('abc'));}

Page 17: Test in action   week 4

Use Factory Function over Multiple Object Creation

• Use a factory functionprotected function create_LogAnalyzer() { // Factory Method $analyzer = new LogAnalyzer(); $analyzer->initialize(); // New API handled here return $analyzer;}public function test_IsValid_InValidContent_ReturnFalse() { $analyzer = $this->create_LogAnalyzer(); // Use factory $this->assertFalse($analyzer->is_valid('abc'));}

Page 18: Test in action   week 4

Use setUp over Multiple Object Creation

• Use setUpprotected function setUp() { // setUp method $this->analyzer = new LogAnalyzer(); $this->analyzer->initialize(); // New API handled here}

public function test_IsValid_InValidContent_ReturnFalse() { $this->assertFalse($this->analyzer->is_valid('abc'));}public function test_IsValid_ValidContent_ReturnFalse() { $this->assertTrue($this->analyzer->is_valid('valid'));}

Page 19: Test in action   week 4

Maintainable setUp

• Maintainable setUp– setUp() should be generic• Cohesion• Initialized object should be used in all tests• Might not be appropriate to arrange mock/stub in

setUp()

– setUp() should be kept his readability

Page 20: Test in action   week 4

Avoid Over Specification in Test

• Over specified in test– Verify internal state/behavior of object– Using mocks when stubs are enough– A test assumes specific order or exact string

matches when it isn’t required.

Page 21: Test in action   week 4

Verify internal state/behavior of object

• Solution– Never verify internal state/behavior– Maybe no need to testpublic function test_Initialize_WhenCalled_SetsDefaultDelimiterIsTabDelimiter(){ $analyzer = new LogAnalyzer(); $this->assertEquals(null, $analyzer->GetInternalDefaultDelimiter() ); $analyzer->Initialize(); $this->assertEquals('\t', $analyzer->GetInternalDefaultDelimiter() );}

Page 22: Test in action   week 4

Avoid Multiple Asserts

• Why not?– Assertion failure will throw exception. Multiple

assertion cannot get all failure point at once– Test multiple thing in one tests

• Solution– Separate tests for different assertion– Use data provider / parameter tests

Page 23: Test in action   week 4

Data Provider Sample

class DataTest extends PHPUnit_Framework_TestCase { /** * @dataProvider provider */ public function testAdd($a, $b, $c) { $this->assertEquals($c, $a + $b); }

public function provider() { return array( array(0, 0, 0), array(0, 1, 1), array(1, 0, 1), array(1, 1, 3) ); }}

• Test Code

Page 24: Test in action   week 4

Readable Tests

• Test Naming• Variable Naming• Good Assert Message• Separate Arrange and Assertion• Mock and Stub Naming

Page 25: Test in action   week 4

Test Naming

• Function Name– Test function name should be

test_<function>_<scenario>_<expect_behavior>– Example• test_escape_evenBackSlashesData_successEscape

Page 26: Test in action   week 4

Variable Name

• Avoid Hard Code in testspublic function test BadlyNamedTest() { $log = new LogAnalyzer(); $result= log.GetLineCount("abc.txt"); $this->assertEquals(-100, result);}

public function test WellNamedTest() { $log = new LogAnalyzer(); $COULD_NOT_READ_FILE = -100; $result= log.GetLineCount("abc.txt"); $this->assertEquals($COULD_NOT_READ_FILE, result);}

Page 27: Test in action   week 4

Good Assertion Message

• Good Assertion Message– Don’t repeat what the built-in test framework

outputs to the console.– Don’t repeat what the test name explains.– If you don’t have anything good to say, don’t say

anything.– Write what should have happened or what failed

Page 28: Test in action   week 4

Separate Arrange and Assertion

• Separate Arrange and Assertion

public function test_BadAssertMessage() { $this->assertEquals(COULD_NOT_READ_FILE, log->GetLineCount("abc.txt") ); }

public function test_GoodAssertMessage() { $result = log->GetLineCount("abc.txt"); $this->assertEquals($COULD_NOT_READ_FILE, $result); }

Page 29: Test in action   week 4

Mock and Stub Naming

• Include “mock” and “stub” in variable name 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 30: Test in action   week 4

Q & A

Page 31: Test in action   week 4

PHPUnit and Selenium

• Use PHPUnit to do– Integration Test– Acceptance Test

• References– PHPUnit Selenium• http://

www.phpunit.de/manual/current/en/selenium.html• http://seleniumhq.org/documentation/

Page 32: Test in action   week 4

PHPUnit and Selenium

• Use PHPUnit and Seleniumclass WebTest extends PHPUnit_Extensions_SeleniumTestCase { protected function setUp() { $this->setBrowser('*firefox'); $this->setBrowserUrl('http://www.example.com/'); }

public function testTitle() { $this->open('http://www.example.com/'); $this->assertTitle('Example WWW Page'); }}

Page 33: Test in action   week 4

PHPUnit and Selenium