Presentation is loading. Please wait.

Presentation is loading. Please wait.

XUnit Test Patterns writing good unit tests Peter Wiles.

Similar presentations


Presentation on theme: "XUnit Test Patterns writing good unit tests Peter Wiles."— Presentation transcript:

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.

53

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/


Download ppt "XUnit Test Patterns writing good unit tests Peter Wiles."

Similar presentations


Ads by Google