Unit tests = maintenance hell ?
-
Upload
thibaud-desodt -
Category
Technology
-
view
33.265 -
download
0
description
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, "[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