writing good unit tests

Post on 11-May-2015

568 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

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

TRANSCRIPT

Writing good unit testsAgileKZN – November 2012© Chillisoft 2012

1

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

2

3

What is a Unit Test?

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

Unit tests are:

1. Small

2. Fast

5

1. Setup2. Exercise3. Verify4. Teardown

The four-phase test

6

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

My System

Unit test

8

My System

SUTUnit test

Fixture

1. Setup

2. Exercise

3. Verify

9

My System

Unit test 4. Teardown

1. Setup

2. Exercise

3. VerifyBoom!

10

Trustworthy

Readable

Maintainable

Note: Thanks to Roy Osherove for this model

11

12

Trustworthy

13

Easy to run

14

Automatically independently verified

15

Same result every time

SUT

Fixture

Unit test

In memory

16

test chaining

17

Peter’s principle for writing unit tests:

use a minimal fresh transient

fixture per test

18

use a minimal fresh transient

fixture per test

the smallest possible fixture you can get away with using

19

use a minimal fresh transient

fixture per test

brand new objects every time, where you can

20

use a minimal fresh transient

fixture per test

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

21

use a minimal fresh transient

fixture per test

the test should create its own fixture

High coverage

Do TDD!

22

23

Readable

24

Well named

use a test naming convention, eg:

Method_ShouldXX()

Method_GivenXX_ShouldYY()

Method_WhenXX_ShouldYY()

25

[Test]public void TestDelimitedTableNameWithSpaces(){

//...}

[Test]public void Generate_GivenTableNameWithSpaces_ShouldDelimitTableName(){

//...}

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

27

Standardised project structure

testcase class per class

test project per project

helpers and bootstrappers

keep integration tests separate

28

Self-contained test

no invisible setup

no teardown

no irrelevant details

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

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());}

31

use a minimal fresh transient

fixture per test

32

Maintainable

33

Be strict:

Less than 5 lines for Arrange

One line for Act

One logical Assert

Test one thing

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);}

35

Isolate your SUT

SUT

FixtureUnit test

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()) );}

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); }

38

Care for your test code

Refactor and use test patterns:

• Factory method

• Custom asserts

• Fluent builders

39

Test public interfaces only

“Use the front

door”

40

further reading

xUnit Test Patterns: Refactoring Test CodeGerard Meszaros

The Art of Unit TestingRoy Osherove

41

Working effectively with legacy codeMichael Feathers

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

even further

reading

42

thanks!

@pwiles

peter.wiles@chillisoft.co.za

http://

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

top related