Writing good unit tests

42
Writing good unit tests AgileKZN – November 2012 © Chillisoft 2012 1

description

A brief presentation on some aspects of good unit tests and anti-patterns to watch out to avoid when writing unit tests.

Transcript of Writing good unit tests

Page 1: Writing good unit tests

Writing good unit testsAgileKZN – November 2012© Chillisoft 2012

1

Page 2: Writing good unit tests

“legacy code is simply code without tests” - Michael Feathers

2

Page 3: Writing good unit tests

3

What is a Unit Test?

Page 4: Writing good unit tests

My System

Acceptance test Acceptance test

Acceptance testAcceptance test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test Unit

test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test Unit

test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit testUnit

testUnit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

4

Page 5: Writing good unit tests

Unit tests are:

1. Small

2. Fast

5

Page 6: Writing good unit tests

1. Setup2. Exercise3. Verify4. Teardown

The four-phase test

6

Page 7: Writing good unit tests

My System

Acceptance test Acceptance test

Acceptance testAcceptance test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Integration test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test Unit

test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test Unit

test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit testUnit

testUnit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

Unit test

7

Page 8: Writing good unit tests

My System

Unit test

8

Page 9: Writing good unit tests

My System

SUTUnit test

Fixture

1. Setup

2. Exercise

3. Verify

9

Page 10: Writing good unit tests

My System

Unit test 4. Teardown

1. Setup

2. Exercise

3. VerifyBoom!

10

Page 11: Writing good unit tests

Trustworthy

Readable

Maintainable

Note: Thanks to Roy Osherove for this model

11

Page 12: Writing good unit tests

12

Trustworthy

Page 13: Writing good unit tests

13

Easy to run

Page 14: Writing good unit tests

14

Automatically independently verified

Page 15: Writing good unit tests

15

Same result every time

SUT

Fixture

Unit test

In memory

Page 16: Writing good unit tests

16

test chaining

Page 17: Writing good unit tests

17

Peter’s principle for writing unit tests:

use a minimal fresh transient

fixture per test

Page 18: Writing good unit tests

18

use a minimal fresh transient

fixture per test

the smallest possible fixture you can get away with using

Page 19: Writing good unit tests

19

use a minimal fresh transient

fixture per test

brand new objects every time, where you can

Page 20: Writing good unit tests

20

use a minimal fresh transient

fixture per test

objects that are chucked after each test, left to the garbage collector

Page 21: Writing good unit tests

21

use a minimal fresh transient

fixture per test

the test should create its own fixture

Page 22: Writing good unit tests

High coverage

Do TDD!

22

Page 23: Writing good unit tests

23

Readable

Page 24: Writing good unit tests

24

Well named

use a test naming convention, eg:

Method_ShouldXX()

Method_GivenXX_ShouldYY()

Method_WhenXX_ShouldYY()

Page 25: Writing good unit tests

25

[Test]public void TestDelimitedTableNameWithSpaces(){

//...}

[Test]public void Generate_GivenTableNameWithSpaces_ShouldDelimitTableName(){

//...}

Page 26: Writing good unit tests

26

Standardised test structure

[Test] public void Append_GivenEmptyString_ShouldNotAddToPrintItems() { // Arrange var document = CreatePrintableDocument(); // Act document.Append(""); // Assert Assert.AreEqual(0, document.PrintItems.Count); }

no teardown

Page 27: Writing good unit tests

27

Standardised project structure

testcase class per class

test project per project

helpers and bootstrappers

keep integration tests separate

Page 28: Writing good unit tests

28

Self-contained test

no invisible setup

no teardown

no irrelevant details

Page 29: Writing good unit tests

29

[Test]public void TestEncryptedPassword(){ Assert.AreEqual(encryptedPassword, encryptedConfig.Password); Assert.AreEqual(encryptedPassword, encryptedConfig.DecryptedPassword); encryptedConfig.SetPrivateKey(rsa.ToXmlString(true)); Assert.AreEqual(password, encryptedConfig.DecryptedPassword);}

invisible setup

Page 30: Writing good unit tests

30

irrelevant details

[Test]public void TestDelimitedTableNameWithSpaces(){ ClassDef.ClassDefs.Clear(); TestAutoInc.LoadClassDefWithAutoIncrementingID(); TestAutoInc bo = new TestAutoInc(); ClassDef.ClassDefs[typeof (TestAutoInc)].TableName = "test autoinc";

DeleteStatementGenerator gen = new DeleteStatementGenerator(bo, DatabaseConnection.CurrentConnection); var statementCol = gen.Generate(); ISqlStatement statement = statementCol.First(); StringAssert.Contains("`test autoinc`", statement.Statement.ToString());}

Page 31: Writing good unit tests

31

use a minimal fresh transient

fixture per test

Page 32: Writing good unit tests

32

Maintainable

Page 33: Writing good unit tests

33

Be strict:

Less than 5 lines for Arrange

One line for Act

One logical Assert

Test one thing

Page 34: Writing good unit tests

34

long, multi-phased test[Test]public void TestDeleteFlagsSetContactPerson(){ ContactPerson myContact = new ContactPerson(); Assert.IsTrue(myContact.Status.IsNew); // this object is new myContact.DateOfBirth = new DateTime(1980, 01, 20); myContact.FirstName = "Bob"; myContact.Surname = "Smith";

myContact.Save(); //save the object to the DB Assert.IsFalse(myContact.Status.IsNew); // this object is saved and thus no longer // new Assert.IsFalse(myContact.Status.IsDeleted);

IPrimaryKey id = myContact.ID; //Save the objectsID so that it can be loaded from the Database Assert.AreEqual(id, myContact.ID); myContact.MarkForDelete(); Assert.IsTrue(myContact.Status.IsDeleted); myContact.Save(); Assert.IsTrue(myContact.Status.IsDeleted); Assert.IsTrue(myContact.Status.IsNew);}

Page 35: Writing good unit tests

35

Isolate your SUT

SUT

FixtureUnit test

Page 36: Writing good unit tests

36

[Test]public void GetCurrentCredentialsString_ShouldReturnDeviceIDFromDeviceState(){ //Arrange var deviceState = Substitute.For<IDeviceState>(); var id = RandomValueGen.GetRandomGuid(); deviceState.GetDeviceID().Returns(id); var credentialsProvider = new CredentialsProvider(deviceState); //Act var result = credentialsProvider.GetCurrentCredentialsString(); //Assert Assert.That(result, Is.EqualTo(id.ToString()) );}

Page 37: Writing good unit tests

37

huge fixture

[TestFixtureSetUp] public void TestFixtureSetup() { SetupDBConnection(); DeleteAllContactPersons(); ClassDef.ClassDefs.Clear(); new Car(); CreateUpdatedContactPersonTestPack(); CreateSaveContactPersonTestPack(); CreateDeletedPersonTestPack(); }

[Test] public void TestActivatorCreate() { Activator.CreateInstance(typeof (ContactPerson), true); }

Page 38: Writing good unit tests

38

Care for your test code

Refactor and use test patterns:

• Factory method

• Custom asserts

• Fluent builders

Page 39: Writing good unit tests

39

Test public interfaces only

“Use the front

door”

Page 40: Writing good unit tests

40

further reading

xUnit Test Patterns: Refactoring Test CodeGerard Meszaros

The Art of Unit TestingRoy Osherove

Page 41: Writing good unit tests

41

Working effectively with legacy codeMichael Feathers

Growing object oriented software, guided by testsSteve Freeman and Nat Pryce

even further

reading

Page 42: Writing good unit tests

42

thanks!

@pwiles

[email protected]

http://

developmentthoughts.wordpress.com/http://www.chillisoft.co.za