Download presentation
Presentation is loading. Please wait.
Published byOswald Marsh Modified over 9 years ago
1
xUnit Test Patterns writing good unit tests Peter Wiles
2
introduction what makes a good unit test? writing good unit tests signs of bad unit tests designing testable software further reading
3
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 practicesTechnical practices the state of agile practices
4
“continuous attention to technical excellence and good design enhances agility” http://agilemanifesto.org/principles.html
5
theory: good unit tests are important.
6
“legacy code is simply code without tests” - Michael Feathers
7
no tests = ? agility bad tests = worse agility good tests = good agility you can’t be truly agile without automated tests
8
tests do not replace good, rigorous software engineering discipline, they enhance it.
9
what makes a good unit test?
10
runs fast helps us localise problems a good unit test - Michael Feathers, again
11
when is a unit test not really a unit test?
12
- Robert C Martin “What makes a clean test? Three things. Readability, readability and readability.”
13
good unit tests are FRIENDS
14
fast lightning fast, as in 50 to 100 per second no IO all in process, in memory
15
robust withstanding changes in unrelated areas of code isolated
16
independent not dependant on external factors, including time
17
examples communicating how to use the class under test readability is key
18
necessary where removing a test would reduce coverage
19
deterministic the result is the same every time
20
specific each test testing one thing
21
deterministic robust fast independent examples necessary specific
22
writing good unit tests some advice
23
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); }
24
write your tests first
25
standardise test structure [Test] public void Append_GivenEmptyString_ShouldNotAddToPrintItems() { // Arrange var document = CreatePrintableDocument(); // Act document.Append(""); // Assert Assert.AreEqual(0, document.PrintItems.Count); }
26
be strict Less than 5 lines for Arrange One line for Act One logical Assert (less than 5 lines)
27
no teardown bare minimum setup
28
use project conventions testcase class per class test project per project helpers and bootstrappers
29
use a test naming convention Method_ShouldXX() Method_GivenXX_ShouldYY() Method_WhenXX_ShouldYY()
30
use a minimal fresh transient fixture per test
31
the smallest possible fixture you can get away with using
32
use a minimal fresh transient fixture per test brand new objects every time, where you can
33
use a minimal fresh transient fixture per test objects that are chucked after each test, left to the garbage collector
34
use a minimal fresh transient fixture per test the test should create its own fixture
35
signs of bad unit tests
36
conditionals if (result == 5)...
37
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); }
38
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); }
39
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); }
40
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()); }
41
external dependencies these are probably integration tests
42
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); }
43
duplication between tests repeated calls to constructors is duplication – refactor this.
44
test chaining
45
s….l….o….w t….e….s.…t….s …
46
no automated build process
47
debugging
48
test-only code in production #if TEST //... #endif if (testing) { //... }
49
randomly failing tests
50
designing testable software
51
use dependency injection aka dependency inversion aka inversion of control
52
Upon construction, give an object everything it needs to do everything it needs to do.
54
use a layered architecture
55
stub/substitute the underlying layers
56
use a testable UI pattern: Model-View-Presenter Model-View-Controller Model-View-ViewModel
57
choose libraries that allow for unit testing. or, build an anti-corruption layer (adapter)
58
test from the outside in check out the BDD talks at CodeLab!
59
further reading xUnit Test Patterns: Refactoring Test Code Gerard Meszaros The Art of Unit Testing Roy Osherove
60
Working effectively with legacy code Michael Feathers Growing object oriented software, guided by tests Steve Freeman and Nat Pryce even further reading
61
thanks! @pwiles peter.wiles@chillisoft.co.za http://developmentthoughts.wordpress.com/
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.