xunit test patterns writing good unit tests peter wiles

61
xUnit Test Patterns writing good unit tests Peter Wiles

Upload: oswald-marsh

Post on 23-Dec-2015

221 views

Category:

Documents


1 download

TRANSCRIPT

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/