34
Refactoring Away from Test Hell @alastairs [email protected] http://www.codebork.com/

Refactoring Away from Test Hell

Embed Size (px)

DESCRIPTION

Your builds have been red for years; maybe they’ve never been green. Someone learned about mocking frameworks at a conference years ago, and now you can’t sneeze without breaking a test. There’s 30 lines of setup and you can’t tell what your tests even do. What happened to the promises of flexible, well-designed code that unit testing and TDD made? In this talk, I cover immediately-applicable ways to refactor your way out of test hell, and demonstrate some open-source libraries to help you clean up your test code, all based on my encounters with dodgy tests from my current and previous jobs. You will come away with practical examples of how to make your tests work for you rather than against you, including recommendations for the correct use of mocking libraries.

Citation preview

Page 1: Refactoring Away from Test Hell

Refactoring Away from

Test Hell@alastairs

[email protected] http://www.codebork.com/

Page 2: Refactoring Away from Test Hell

The Graduate

Page 3: Refactoring Away from Test Hell

The Junior

Page 4: Refactoring Away from Test Hell

The Software Engineer

Page 5: Refactoring Away from Test Hell

Tests are your parachute

Page 6: Refactoring Away from Test Hell
Page 7: Refactoring Away from Test Hell

•Getting to Green

•Use Mocks Wisely

•Document the System

Page 8: Refactoring Away from Test Hell

Getting to Green

Page 9: Refactoring Away from Test Hell

Do what you need to get there

1. Invest in the infrastructure

2.Fake the infrastructure

3. Isolate yourself from the infrastructure

Page 10: Refactoring Away from Test Hell

Invest in the infrastructure

• Add more build agents

• Run tests in parallel

• Beefier hardware

• Fix infrastructure issues

Page 11: Refactoring Away from Test Hell

Fake the infrastructure

• Introduce a seam

• A place where you can alter behaviour without editing in that place

• Use Value Objects for your inputs and outputs

• Try Record-Replay

Page 12: Refactoring Away from Test Hell

Record-Replay

Short-term hack!

• Fake the seam interface

• Save requests to disk

• Respond immediately if a known request

• Compare artefacts against a known-good version

Page 13: Refactoring Away from Test Hell

Isolate yourself from the infrastructure

• Refactor your seams

• Introduce mocks

Page 14: Refactoring Away from Test Hell

Use Mocks Wisely

Page 15: Refactoring Away from Test Hell

Mocking Rules of Thumb

• Don’t mix mocks and stubs

• Don’t nest mocks

• Use strict mocks

• Only mock types you own

Page 16: Refactoring Away from Test Hell

It’s a Unit of Behaviour, Dammit!

• Test your interface, not your implementation

• Use Collaboration and Contract tests (http://j.mp/10UFgOS)

Page 17: Refactoring Away from Test Hell

[Test]public void Moq_Example_With_A_Mock(){ // Arrange var mock = new Mock<IProductRepository>(MockBehavior.Strict); mock.Setup(r => r.Save(pen)); // Test fails if not included var sut = new AddProductsCommand(mock.Object);

// Act sut.Add(pen);

// Assert mock.Verify(r => r.Save(pen)); // Behaviour Verification}

Page 18: Refactoring Away from Test Hell

[Test]public void Moq_Example_With_A_Stub(){ // Arrange    var stub = new Mock<IProductRepository>(MockBehavior.Strict);    stub.Setup(r => r.GetProduct(1)).Returns(pen); // Pre-condition    var sut = new AddProductsCommand(stub.Object);

// Act    var actual = sut.Get(1);

// Assert    var expected = pen;    Assert.That(actual, Is.EqualTo(expected));}

Page 19: Refactoring Away from Test Hell

Document the System

Page 20: Refactoring Away from Test Hell

General tips

• Use Descriptive And Meaningful Phrases

• Use a defined structure with conventions

• Make anonymous data obviously anonymousvar anonymousText = "Anonymous Text";

• Use randomised data for large input spaces

• Keep to a Cyclomatic Complexity of 1

Page 21: Refactoring Away from Test Hell

SUT Factory

• Decouples tests from construction logic

• Protects you from changes to the constructor signature

• Consider the Fixture Object pattern

SUT: System Under Test

Page 22: Refactoring Away from Test Hell

Test Builders

• A Domain-Specific Language in your tests

• Clarify intent

• Use for anything with

• Non-trivial construction

• Varied setup

• Test Data

• The SUT

Page 23: Refactoring Away from Test Hell

[Fact]public void Placing_An_Order_Adds_The_Order_To_The_Customers_Account(){ // Arrange var customer = new Customer( 1, // Id "Joe", "Bloggs", // Name "10 City Road", "Staines", "Middlesex", "AB1 2CD" // Address ); var order = new Order(1, customer);

// Act, Assert: not interesting for this example}

Page 24: Refactoring Away from Test Hell

[Fact]public void Placing_An_Order_Adds_The_Order_To_The_Customers_Account(){ // Arrange var customer = ACustomer() .WithGivenName("Joe") .WithFamilyName("Bloggs") .Build(); var order = AnOrder().PlacedBy(customer).Build();

// Act, Assert: not interesting for this example}

Page 25: Refactoring Away from Test Hell

Bob the Builder

• Maintains Fluent syntax

• Reduces boilerplate

• Install-Package BobTheBuilder

Page 26: Refactoring Away from Test Hell

using BobTheBuilder;

[Fact]public void Placing_An_Order_Adds_The_Order_To_The_Customers_Account(){ // Arrange var customer = A.BuilderFor<Customer>() .WithGivenName("Joe") .WithFamilyName("Bloggs") .Build(); var order = new Order(1, customer);

// Act, Assert: not interesting for this example}

Page 27: Refactoring Away from Test Hell

using BobTheBuilder;

[Fact]public void Placing_An_Order_Adds_The_Order_To_The_Customers_Account(){ // Arrange Customer customer = A.BuilderFor<Customer>() .With(givenName: "Joe", familyName: "Bloggs"); var order = new Order(1, customer);

// Act, Assert: not interesting for this example}

Page 28: Refactoring Away from Test Hell

Assertions

• Keep to one logical assertion per test

• Aim for Equality assertions

• Write custom constraints / matchers

• Consider the Fixture Object pattern

Page 29: Refactoring Away from Test Hell

Test-Specific Equality

• IEqualityComparer<T>

• Resemblance

• Likeness

Page 30: Refactoring Away from Test Hell

AutoFixture

• Automates many of the techniques described here

• Install-Package AutoFixture

• Support for all popular mocking frameworks

Page 31: Refactoring Away from Test Hell

[Test, AutoData]public void IntroductoryTest(int expectedNumber, MyClass sut){ int result = sut.Echo(expectedNumber);    Assert.Equal(expectedNumber, result);}

Page 32: Refactoring Away from Test Hell

• Getting to Green

• Do what you need to get there

• Use Mocks Wisely

• Don’t nest mocks

• Use strict mocks

• Write Contract and Collaboration Tests

• Document the System

• SUT Factory

• Test Builders

• Assertions

Page 33: Refactoring Away from Test Hell

References and Resourceshttp://j.mp/refactoringTestHell

xUnit Patterns (Gerard Meszaros): http://j.mp/11bV67X

Integration Tests are a Scam (J B Rainsberger): http://j.mp/10UFgOS

Boundaries (Gary Berhardt): http://j.mp/1o18XbT

Advanced Unit Testing (Mark Seemann, Pluralsight): http://j.mp/psAdvUT

Page 34: Refactoring Away from Test Hell

Any questions?@alastairs

[email protected] http://www.codebork.com/