Upload
thibaud-desodt
View
33.270
Download
0
Embed Size (px)
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…
Citation preview
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, "[email protected]"); var expectedUser = new User(13, "[email protected]");
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