Unit tests = maintenance hell ?

Post on 06-May-2015

33.265 views 0 download

description

The slides or my talk at SoftShake (soft-shake 2013) about unit test maintenance. The code can be found here : https://github.com/tsimbalar/UnitTestsMaintenanceHell Abstract (in French, sorry ...) : “Oui, les tests unitaires, c’est cool, mais à chaque fois que je fais une petite modification dans le code, je dois réparer tous mes tests, c’est pénible et ça me prend un temps fou! Alors j’hésite à laisser tomber ma suite de tests, ou alors je fais l’impasse sur ce refactoring …” Vous avez déjà entendu ça, non ? Quand on se met à écrire des tests unitaires (que ce soit avant ou après le code à tester), au début, c’est la galère, puis, peu à peu, avec l’habitude et l’expérience, on les écrit mieux et plus rapidement … mais quand il s’agit de modifier du code existant, on a quand même l’impression de se prendre les pieds dans les tests. Dans cette session, je vous présenterai quelques techniques, trucs et outils pour écrire des tests plus maintenables et vous sentir moins gênés lors des refactorings du code. Cette présentation se basera sur des exemples de code et des démos (en C# et avec Visual Studio) Les tests apportent une forte valeur ajoutée à nos projets, ce qu’il faut, c’est essayer d’en réduire le coût…

Transcript of Unit tests = maintenance hell ?

Unit Tests = Maintenance Hell ?

Thibaud DESODT@tsimbalar

This talk

• About Unit-Testing– Why we do it – Why it can slow you down– How to ease the maintenance

• Include : – .NET-based examples / tools– … can be applied to other platforms

UNIT TESTS

Value of Unit tests

Codebase size

Valu

e o

f Uni

t tes

ts

“toy” project

s

• Benefits– Avoid introducing bugs– Executable documentation– Regression detection– Know when to stop– Reduce fear / allow

refactoring– …

Unit-tests help build CONFIDENCE

But when the codebase grows …

Quantity of Test Code

• Prod Code vs Test Code Ratio– from 1:1 to 1:5 !

• Muliplied efforts :– New Code – Changing existing code

More Test Code than Prod Code !

Lower Quality of Test Code

• Viewed as less important than Prod Code– “Write Once” – Duplication / Copy+Paste– Less refactoring / improvements– Hard to read/understand

• Technical Debt reduction has less priority

Cost of Unit Tests

Codebase size

Cost

of U

nit t

ests

Unit-tests introduce FRICTION

ROI

Codebase size

Valu

e vs

Cos

t of

Uni

t tes

ts

WTF?

Unit Tests = Maintenance Hell ?

It can be … but it does not have to !

ROI

Codebase size

Valu

e vs

Cos

t of

Uni

t tes

ts

WTF?

Solution 1 : give up on Unit Tests

“Legacy Code is code without Tests”Michael Feathers

Working Effectively with Legacy Code

Codebase size

Mai

ntai

nabi

lity

of c

odeb

ase

Solution 2 : reduce the cost of test maintenance !

Codebase size

Valu

e vs

Cos

t of

Uni

t tes

ts

WIN !

REDUCING TEST MAINTENANCE COSTS

TIP #1 : GET PROPER TOOLS

Tools to manage thousands of tests

– Exploring tests / results– Run All– Run/Debug Current Test– Run All Tests in current

Test Class – Re-run last executed

tests– …

• VS2012 Test Explorer– Just kidding !

• Telerik JustCode• JetBrains ReSharper

Continuous Testing

• NCrunch !– Runs tests continuously– Reports results without rebuilding or saving the files !– Impacted tests are run first– Tests run in parallel

• Expensive but worth it ! (159$/289$)

– Give it a try http://www.ncrunch.net/– Free alternative : http://continuoustests.com/

TIP #2 : GET ORGANIZED

Define conventionsConvention Choice

Test Projects [ProjectNamespace].TestsTest Classes One/class to test: [TestedClass]TestTest Methods [MethodName]_with_[Condition]_[ExpectedOutcom

e]Structure Arrange/Act/Assert or Given/When/ThenHelper methods In Test Class (private static)

In Test Project in namespace TestUtilsIn dedicated project « Test Support »

Which convention you choose does not matter, as long as it’s used by everybody !

Use « standard » variable names / prefixes

• Common concepts :– sut (System Under Test)– actual (result of the action)– expected (expected value for the result)

• Benefits : – Makes tests more homogenous – No need to think of it when writing the test– Resistant to class renaming !

Typical structure

[TestMethod]public void SomeMethod_with_SomeConditions_must_DoSomething(){ // Arrange var sut = MakeSut(); var expected = "what it should return"; // Act var actual = sut.DoSomething();

// Assert Assert.AreEqual(expected, actual, "Should have done something");}

TIP #3 : MAKE WRITING TESTS CHEAP AND EASY !

Use templates / snippets

• Commonly created items :– Test Class– Test Methods – Test Helper Methods

• … and share them with the team !

• Examples : – aaa, aae, any …

TIP #4 : MAKE YOUR TESTS REFACTORING-FRIENDLY

It will change !

• In 3 minutes / days / weeks … • Be ready !

“The only constant is change”Heraclitus of Ephesus

5th century BC

Constructors

• Dependency Injection entry point– Changes when dependencies are added/removed– Impact in every test !

• Encapsulate it in a helper Method

MakeSut()

– Default value for dependencies– Adding dependencies does not impact tests

private static ClassToTest MakeSut( IDependency1 dep1 = null, IDependency2 dep2 = null){

dep1 = dep1 ?? new Mock<IDependency1>().Object; dep2 = dep2 ?? new Mock<IDependency2>().Object;

return new ClassToTest(dep1, dep2);}

Test Data creation

• Classes will change– New properties– Updated constructors ….

• Automate generation of « auxiliary » test objects

AutoFixture

• Creates instances of classes– Populates constructor

arguments– Populates properties– Recursive– Values derived from

Property names

https://github.com/AutoFixture/AutoFixture/wiki/Cheat-Sheet

var fixture = new Fixture();var user = fixture.Create<User>();

> Install-Package AutoFixture

Test-specific comparison

• Comparing objects is common– Actual vs expected– Should not override Equals() for the tests

• Adding properties can break comparison

Semantic Comparison var actualUser = new User(12, "john@rambo.com"); var expectedUser = new User(13, "tibo@desodt.com");

actualUser.AsSource().OfLikeness<User>().ShouldEqual(expectedUser); Ploeh.SemanticComparison.LikenessException: The provided value

ProductionCode.Lib.Data.User did not match the expected value ProductionCode.Lib.Data.User. The following members did not match:- Id.- EmailAddress.

actualUser.AsSource().OfLikeness<User>() .Without(u=> u.Id) .ShouldEqual(expectedUser);

> Install-Package SemanticComparison

TO CONCLUDE

Working with many Unit Tests

• … is hard• Requires extra care

– Up-front– During code

maintenance

• But pain can be reduced by:– Appropriate tools– Good habits

TIPS#1 Proper Tools

– Good Test Runner– Continuous Tests

#2 Get organized– Conventions– Naming

#3 Make Writing Tests Cheap and easy

– Templates/snippets

#4 Prepare for changes– Encapsulate constructors– Automate object Creation– Automate object comparison

Recommended read• xUnit Test Patterns: Refactoring Test

Code by Gerard Meszaros – http://xunitpatterns.com/

• Zero-Friction TDD by Mark Seemann– http://blog.ploeh.dk/2009/01/28/Zero-

FrictionTDD/

THANKS !QUESTIONS ?

Thibaud DESODT@tsimbalar