XUnit Test Patterns writing good unit tests Peter Wiles.

61
xUnit Test Patterns writing good unit tests Peter Wiles

Transcript of XUnit Test Patterns writing good unit tests Peter Wiles.

Page 1: XUnit Test Patterns writing good unit tests Peter Wiles.

xUnit Test Patternswriting good unit

tests

Peter Wiles

Page 2: XUnit Test Patterns writing good unit tests Peter Wiles.

introduction

what makes a good unit test?

writing good unit tests

signs of bad unit tests

designing testable software

further reading

Page 3: XUnit Test Patterns writing good unit tests Peter Wiles.

http://www.versionone.com/state_of_agile_development_survey/11/

Daily standup – 78%Iteration planning – 74%Release planning – 65%Burndown – 64%Retrospectives – 64%Velocity – 52%

Unit testing – 70%Continuous Integration – 54%Automated builds – 53%Coding standards – 51%Refactoring – 48%Test driven development – 38%

Management practices

Technical practices

the state of agile practices

Page 4: XUnit Test Patterns writing good unit tests Peter Wiles.

“continuous attention to technical excellence and good design enhances agility”

http://agilemanifesto.org/principles.html

Page 5: XUnit Test Patterns writing good unit tests Peter Wiles.

theory:good unit tests are important.

Page 6: XUnit Test Patterns writing good unit tests Peter Wiles.

“legacy code is simply code without tests”

- Michael Feathers

Page 7: XUnit Test Patterns writing good unit tests Peter Wiles.

no tests = ? agility

bad tests = worse agility

good tests = good agility

you can’t be truly agile without automated tests

Page 8: XUnit Test Patterns writing good unit tests Peter Wiles.

tests do not replace good, rigorous software engineering discipline, they enhance it.

Page 9: XUnit Test Patterns writing good unit tests Peter Wiles.

what makes a good

unit test?

Page 10: XUnit Test Patterns writing good unit tests Peter Wiles.

runs fast

helps us localise

problems

a good unit test

- Michael Feathers, again

Page 11: XUnit Test Patterns writing good unit tests Peter Wiles.

when is a unit test not

really a unit test?

Page 12: XUnit Test Patterns writing good unit tests Peter Wiles.

- Robert C Martin

“What makes a clean test?

Three things. Readability,

readability and readability.”

Page 13: XUnit Test Patterns writing good unit tests Peter Wiles.

good unit tests are

FRIENDS

Page 14: XUnit Test Patterns writing good unit tests Peter Wiles.

fastlightning fast, as in 50 to 100 per second

no IOall in process, in memory

Page 15: XUnit Test Patterns writing good unit tests Peter Wiles.

robustwithstanding changes in unrelated areas of code

isolated

Page 16: XUnit Test Patterns writing good unit tests Peter Wiles.

independent

not dependant on external factors, including time

Page 17: XUnit Test Patterns writing good unit tests Peter Wiles.

examplescommunicating how to use the class under test

readability is key

Page 18: XUnit Test Patterns writing good unit tests Peter Wiles.

necessary

where removing a test would reduce coverage

Page 19: XUnit Test Patterns writing good unit tests Peter Wiles.

deterministic

the result is the same every time

Page 20: XUnit Test Patterns writing good unit tests Peter Wiles.

specific

each test testing one thing

Page 21: XUnit Test Patterns writing good unit tests Peter Wiles.

deterministic

robustfast

independentexamplesnecessary

specific

Page 22: XUnit Test Patterns writing good unit tests Peter Wiles.

writing good unit tests

some advice

Page 23: XUnit Test Patterns writing good unit tests Peter Wiles.

use an xUnit

framework[TestFixture]public class TestCalculator{ [Test] public void Sum() { var calculator = new Calculator();

var sum = calculator.Sum(5, 4);

Assert.AreEqual(9, sum); }}

Page 24: XUnit Test Patterns writing good unit tests Peter Wiles.

write your tests first

Page 25: XUnit Test Patterns writing good unit tests Peter Wiles.

standardise test

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

Page 26: XUnit Test Patterns writing good unit tests Peter Wiles.

be strict

Less than 5 lines for Arrange

One line for Act

One logical Assert (less than 5

lines)

Page 27: XUnit Test Patterns writing good unit tests Peter Wiles.

no teardown

bare minimum

setup

Page 28: XUnit Test Patterns writing good unit tests Peter Wiles.

use project conventions

testcase class per class

test project per project

helpers and bootstrappers

Page 29: XUnit Test Patterns writing good unit tests Peter Wiles.

use a test naming convention

Method_ShouldXX()

Method_GivenXX_ShouldYY()

Method_WhenXX_ShouldYY()

Page 30: XUnit Test Patterns writing good unit tests Peter Wiles.

use a minimal fresh transient

fixture per test

Page 31: XUnit Test Patterns writing good unit tests Peter Wiles.

use a minimal fresh transient

fixture per test

the smallest possible fixture you can get away with using

Page 32: XUnit Test Patterns writing good unit tests Peter Wiles.

use a minimal fresh transient

fixture per test

brand new objects every time, where you can

Page 33: XUnit Test Patterns writing good unit tests Peter Wiles.

use a minimal fresh transient

fixture per test

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

Page 34: XUnit Test Patterns writing good unit tests Peter Wiles.

use a minimal fresh transient

fixture per test

the test should create its own fixture

Page 35: XUnit Test Patterns writing good unit tests Peter Wiles.

signs of bad unit

tests

Page 36: XUnit Test Patterns writing good unit tests Peter Wiles.

conditionals

if (result == 5) ...

Page 37: XUnit Test Patterns writing good unit tests Peter Wiles.

long test

methods[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 38: XUnit Test Patterns writing good unit tests Peter Wiles.

invisible setup

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

Page 39: XUnit Test Patterns writing good unit tests Peter Wiles.

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 40: XUnit Test Patterns writing good unit tests Peter Wiles.

too much

information[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 41: XUnit Test Patterns writing good unit tests Peter Wiles.

external

dependencies

these are probably

integration tests

Page 42: XUnit Test Patterns writing good unit tests Peter Wiles.

too many asserts[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 43: XUnit Test Patterns writing good unit tests Peter Wiles.

duplication between

testsrepeated calls to constructors is

duplication – refactor this.

Page 44: XUnit Test Patterns writing good unit tests Peter Wiles.

test chaining

Page 45: XUnit Test Patterns writing good unit tests Peter Wiles.

s….l….o….w

t….e….s.…t….s

Page 46: XUnit Test Patterns writing good unit tests Peter Wiles.

no automated build

process

Page 47: XUnit Test Patterns writing good unit tests Peter Wiles.

debugging

Page 48: XUnit Test Patterns writing good unit tests Peter Wiles.

test-only code in

production#if TEST//...#endif

if (testing) { //... }

Page 49: XUnit Test Patterns writing good unit tests Peter Wiles.

randomly failing tests

Page 50: XUnit Test Patterns writing good unit tests Peter Wiles.

designing testable

software

Page 51: XUnit Test Patterns writing good unit tests Peter Wiles.

use dependency injection

aka dependency

inversionaka inversion of control

Page 52: XUnit Test Patterns writing good unit tests Peter Wiles.

Upon construction, give an

object everything it needs to

do everything it needs to do.

Page 53: XUnit Test Patterns writing good unit tests Peter Wiles.
Page 54: XUnit Test Patterns writing good unit tests Peter Wiles.

use a layered

architecture

Page 55: XUnit Test Patterns writing good unit tests Peter Wiles.

stub/substitute the

underlying layers

Page 56: XUnit Test Patterns writing good unit tests Peter Wiles.

use a testable UI pattern:

Model-View-Presenter

Model-View-Controller

Model-View-ViewModel

Page 57: XUnit Test Patterns writing good unit tests Peter Wiles.

choose libraries that

allow for unit testing.

or, build an anti-corruption layer (adapter)

Page 58: XUnit Test Patterns writing good unit tests Peter Wiles.

test from the outside in

check out the BDD talks at CodeLab!

Page 59: XUnit Test Patterns writing good unit tests Peter Wiles.

further reading

xUnit Test Patterns: Refactoring Test CodeGerard Meszaros

The Art of Unit TestingRoy Osherove

Page 60: XUnit Test Patterns writing good unit tests Peter Wiles.

Working effectively with legacy codeMichael Feathers

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

even further

reading

Page 61: XUnit Test Patterns writing good unit tests Peter Wiles.

thanks!

@pwiles

[email protected]

http://

developmentthoughts.wordpress.com/