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…
08448380779 Call Girls In Civil Lines Women Seeking Men
Unit tests = maintenance hell ?
1. Unit Tests = Maintenance Hell ?
Thibaud DESODT
@tsimbalar
2. 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
4. Value of Unit tests
• Benefits
Value of Unit tests
–
–
–
–
–
“toy”
projects
Avoid introducing bugs
Executable documentation
Regression detection
Know when to stop
Reduce fear / allow
refactoring
–…
Unit-tests help build CONFIDENCE
Codebase size
6. Quantity of Test Code
• Prod Code vs Test Code Ratio
– from 1:1 to 1:5 !
More Test Code than Prod Code !
• Muliplied efforts :
– New Code
– Changing existing code
7. 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
8. Cost of Unit tests
Cost of Unit Tests
Unit-tests introduce FRICTION
Codebase size
12. Maintainability of codebase
Solution 1 : give up on Unit Tests
“Legacy Code is code without Tests”
Michael Feathers
Working Effectively with Legacy Code
Codebase size
13. WIN !
Value vs Cost of Unit tests
Solution 2 : reduce the cost
of test maintenance !
Codebase size
16. 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
17. 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/
19. Define conventions
Convention
Choice
Test Projects
[ProjectNamespace].Tests
Test Classes
One/class to test: [TestedClass]Test
Test Methods
[MethodName]_with_[Condition]_[ExpectedOutcome]
Structure
Arrange/Act/Assert or Given/When/Then
Helper methods In Test Class (private static)
In Test Project in namespace TestUtils
In dedicated project « Test Support »
Which convention you choose does not matter,
as long as it’s used by everybody !
20. 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 !
21. 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");
}
23. Use templates / snippets
• Commonly created items :
– Test Class
– Test Methods
– Test Helper Methods
• … and share them with the team !
• Examples :
– aaa, aae, any …
25. It will change !
“The only constant is change”
Heraclitus of Ephesus
5th century BC
• In 3 minutes / days / weeks …
• Be ready !
26. Constructors
• Dependency Injection entry point
– Changes when dependencies are added/removed
– Impact in every test !
• Encapsulate it in a helper Method
27. MakeSut()
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);
}
– Default value for dependencies
– Adding dependencies does not impact tests
28. Test Data creation
• Classes will change
– New properties
– Updated constructors ….
• Automate generation of « auxiliary » test
objects
29. AutoFixture
• Creates instances of
classes
var fixture = new Fixture();
var user = fixture.Create<User>();
– Populates constructor
arguments
– Populates properties
– Recursive
– Values derived from
Property names
> Install-Package AutoFixture
https://github.com/AutoFixture/AutoFixture/wiki/Cheat-Sheet
30. Test-specific comparison
• Comparing objects is common
– Actual vs expected
– Should not override Equals() for the tests
• Adding properties can break comparison
31. 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
33. 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
34. 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/ZeroFrictionTDD/
French software developer.NET web-apps for the French GovernmentAgile software team using Scrum … in BelgiumColleagues say I have some kind of obsession with Unit-Testing … they may be rightInterest in automated testing / unit-testing in particular 2-3 years agoI struggled a bit at the beginning, unit-testing + TDD in a legacy project. hard, but started to feel comfortable - adding = more fun and exciting. Triedto improve my skills, reading a lot about the topic and experimentingMy name is Thibaud DESODT , I am a French software developper, currently working in Belgium for the French government. I work in a small Scrum team on the development of .NET-based web applications. According to my colleagues, I have some kind of obsession about unit tests ... the truth is they may be right .... I started to get interested in automated testing and in unit-testing in particular 2-3 years ago. As most of us did, I struggled a bit at the beginning, trying to apply unit-testing and TDD to the current legacy project. It was hard, but I soon started to feel more comfortable and it made adding features to the system a bit more fun and exciting. Since then, I've tried to improve my skills, reading a lot about the topic, and trying new things in the projects I work on.
Enough about meIt’s not about : Integration Testing, Manual Testing, TDD … When it feels like they are slowing you down … how to reduce that feeling and get more productive on codebases with lots of unit-testsTargets people with previous knowledge of Unit-testingenough about me .... this talk is about Unit-Testing (not integration testing, not manual testing ...) ... when it starts to feel like they are slowing you down, and how to avoid or at least reduce that feeling. I'll talk a bit about unit-testing in general and then I'll try to provide tips to improve test maintenance. Because I am a .NET developer, most of the code and tools I'm going to show are related to the .NET ecostystem, but I guess most of the ideas can apply to other development platform ... (oh and yes, there won't be any VB.NET , sorry ;) ).
Shorter Feedback loops require more automationMore automation requires more confidence
With small projects, Unit Tests don’t provide so much valueBut as soon as the codebase grows, you need confidence to move forward … and Unit Test’s value gets bigger
Problems with unit tests over time : All those unit tests may slow us downit can feel like we struggle / they slow us down ...mostly because it's more code to maintain ! some kind of duplication
there are lots because they are the most granular part and least fragile ! (test pyramid)Just the number makes it hard to manage and have a good view of all the unit tests in the project
usually more Loc than production code 1:1 to 1:5 !Each line of code in prod means at least 2 lines of code to maintain
Writing tests is actually expensive !
Writing tests is actually expensive !
Let's review some best/worst practices to see WHAT TO DO and WHAT NOT TO DO when working with code bases with lots of unit tests .... in some random order ... some very specific and technical, some more general
Once you reach several hundred/thousands unit tests, you need good tools to manage / run your tests.Does anybody use the default Visual Studio Test Runner ? do you like it ?ReSharper Test-runner ! . or others (CodeRush ? Driven ? )You want to be able to : - run current test- run tests in current test class- run tests in current namespace- run tests in current project- run ALL tests+ re-run last run
Bonus : do not forget to run unit tests !Best way = run them constantly NCrunch ! show how it works(alternative : MightyMoose ?)
Let's review some best/worst practices to see WHAT TO DO and WHAT NOT TO DO when working with code bases with lots of unit tests .... in some random order ... some very specific and technical, some more general
It’s hard to maintain an unheterogenous Test Code Base !It’s hard to findwhatyour are looking forIt’s hard to add tests in it
Quick demo des Snippets
Who uses dependency injection ?UserNotificationServicewhichsends an email to a user …Wewantit to alsosend an SMS-> introduce a new constructor argument
Ajout d’un paramètre PhoneNumber sur User … impact sur les tests alors que la valeur a peu d’importance
Envoi du SMS … si on ajoute une propriété à SMS que devient la comparaison ?