[email protected]://www.gerritforge.comTwitter: @gitenterprise
TDDDead or Alive?
Luca Milanesio
GerritForge
http://visionwidget.com/images/2010-4/0405/Wanted_Poster.jpgPlatinum Sponsor
2 .io
About me
Luca MilanesioCo-founder of GerritForge
over 20 years of experience in Agile Development SCM and ALM worldwide
Contributor to Jenkins since 2007 (and previously Hudson)
Git SCM mentor for the Enterprise since 2009
Contributor to Gerrit Code Review community since 2011
3 .io
Agenda Why? TDD-induced damage Evangelist vs. Professional Reboot Professional TDD Learning to write
4 .io
TDD is 20 years old
http://www.macqueen.us/smalltalkReport/ST/91_95/SMAL0402.PDF
5 .io
TDD has been widely adopted
http://www.indeed.com/jobtrends?q=test+driven+development&relative=1
6 .io
Hold on … what's that ?
http://www.indeed.com/jobtrends?q=test+driven+development&relative=1
?
7 .io
… and that ?
http://martinfowler.com/articles/is-tdd-dead/
8 .io
Test-induced design damageBy David Heinemeier Hansson on April 29, 2014
http://david.heinemeierhansson.com/2014/test-induced-design-damage.html
"Code that's hard to test in isolation is poorly designed", goes a common TDD maxim. Isolation meaning free of dependent context and separated from collaborators, especially "slow" ones like database or file IO. The prevalent definition of "unit" in unit testing (though not everyone agrees with this).
This is some times true. Some times finding it difficult to test points to a design smell. It may be tangled responsibilities or whatever. But that's a far ways off from declaring that hard-to-unit-test code is always poorly designed, and always in need of repair. That you cannot have well-designed code that is hard to unit test.
It's from this unfortunate maxim that much of the test-induced design damage flows. Such damage is defined as changes to your code that either facilitates a)
easier test-first, b) speedy tests, or c) unit tests, but does so by harming the clarity of the code through — usually through needless indirection and conceptual overhead. Code that is warped out of shape solely to accommodate testing objectives.
9 .io
TDD EXAMPLE DAMAGE ON A
LARGE PROJECT
https://www.flickr.com/photos/martinluff/5475631631
10 .io
Code metrics of one service
340 Java Classes880 Tests158 Mocks
99.5% Code Coverage
11 .io
TDD seniority and practices
Devs with two digits' years' experienceTDD applied with discipline from start
Code constantly covered by testsBuild time: 2 minutes (~ 130 msec per
test)
12 .io
Test example
@Test public void shouldGetNewDTOFromEntity() { // Given DTO expectedDTO = DTOFixture.getDefaultRewardWithOneVariant().build(); Reward entity = VariantRewardFixture.getDefaultRewardWithOneVariant().build();
// When DTO variantDTO = entity.transformToDTO();
// Then assertThat(variantDTO, is(not(nullValue()))); assertThat(variantDTO, is(reflectionEquals(expectedDTO))); }
13 .io
Code example
public DTO transformToDTO() { String id = getId() == null ? null : getId().toString();
DTO dto = DTO.forVariants(); dto.setId(id); dto.setName(getName()); dto.setDescription(getDescription()); dto.setShortDescription(getShortDescription()); dto.setStatus(getStatus()); dto.setRewardItemType(getRewardItemType()); dto.setDeliveryMechanism(getDeliveryMechanism()); dto.setTermsAndConditions(getTermsAndConditions()); dto.setStockLimited(isStockLimited()); dto.setImages(transformImagesToDTOs(getImages()));
for (Variant variant : getVariants()) { dto.addVariant(variant.transformToDTO()); }
return dto; }
14 .io
THAT'S GOOD TDDOR NOT?
http://icongal.com/gallery/icon/17045/256/thumbs_up_thumbs_up_vote_like
15 .io
Features velocity
For every new feature we did:End-to-End user-journeys acceptance testService-to-Service integration testComponent-to-component integration testsUnit tests (with mocks)
16 .io
… and their planning meeting
[TechLead] "How many points for this story ?"[Devs] "2 .. 5 .. 5 .. 5"[TechLead] "5 ? C'mon … for that small change?"[Devs] "Change is trivial, but it will take some time
to amend all the tests and getting a green build"
17 .io
But with more quality?
At the beginning yes Project grew, tests base grew as well Breaking tests became the norm Less frequent refactor, for not breaking
tests Tech debt increased over time
18 .io
WHAT WENT WRONG?
https://www.flickr.com/photos/crystalflickr/145082274
19 .io
I AM NOT TDD EVANGELIST
Evangelists are "relaying information about a particular set of beliefs with the intention of converting the recipient"
http://en.wikipedia.org/wiki/Technology_evangelist
http://school.point2educate.com/wp-content/uploads/2014/03/Book-Red.png
20 .io
I AMTDD
PROFESSIONALProfessionals are "members of the profession with the particular
knowledge and skills necessary to perform the role of that profession"http://en.wikipedia.org/wiki/Professional
http://www.levantar.co.uk/images/images/Lean_Legal_Professional_Firms.jpg
21 .io
PROFESSIONAL TDD
REBOOT
http://png-2.findicons.com/files/icons/75/i_like_buttons_3a/512/perspective_button_standby.png
22 .io
Step 1 – TEST RED
Write the test, without any code written yetRED: test fails
(compilation errors are considered failures as well)
23 .io
Step 2 – TEST GREEN
Make test work as quickly as possibleGREEN: test passes
(code satisfies the test assertions)
24 .io
Step 3 – CODE REFACTOR
Keep the test code unchangedRework the implementation to make it
clean, DRY, flexibleGREEN: new code passes the test
(reworked code behaves exactly as before)
25 .io
TESTIS THE HEAD
OF ALL TDD CYCLE
http://www.improveit.mx/Imagenes/tdd_esquema_en.jpg
26 .io
TDD ASSUMES
THAT TEST IS
CORRECT
http://www.wisteriaformations.co.uk/articles/wp-content/uploads/2011/09/Company-Formation-15.jpg
27 .io
BUT WHAT IF TEST
IS NOT 100% CORRECT?
http://blogs.warwick.ac.uk/images/fsavva/2012/04/16/question-mark-man.jpg?maxWidth=500
28 .io
ANOTHER POSSIBLE DIFFERENT REALITY
http://www.gamerzines.com/wp-content/uploads/2014/03/morpheus-chair.jpg
29 .io
Step 1 – TEST RED-ish
Write the [incorrect] test, no code exists yetRED: fails, but maybe the test is wrong?
(nothing works, who can tell what is really wrong?)
?
30 .io
Step 2 – TEST GREEN-ish
Make test work as quickly as possibleGREEN: [incorrect] test passes
(is code correct or wrong? nobody really knows)
?
31 .io
Step 3 – CODE REFACTOR-ish
Keep the test code unchangedRework the implementation to make it
clean, DRY, flexibleGREEN: reworked code passes the tests
(was the code correct before? Is the code still correct after?)
?
32 .io
?
What is TESTING?
How to TEST the length of a table?
Use your foot Or a RULER?
33 .io
How can I test the TEST?
How do you test a ruler?
?
Your thumb?
Or a digital caliper?
34 .io
And how can I test the test of a TEST?
How do you test a digital caliper?YOU DON'T
[One meter is express in terms of speed of light]
35 .io
Test and accuracy
TEST has to be MORE ACCURATE
than CODE
36 .io
Test and confidence
TEST cannot be testedbut we have
CONFIDENCE to beVALID
K = 299,792,458 m / s
37 .io
Can this test @Test public void shouldRedisplayEditFormWhenErrorAndUserKeyExists() {
boolean partnerTokenExists = true; setupForRedisplayEditFormWhenError(partnerTokenExists);
ModelAndView mav = partnerController.update(RESOURCE_URI, mockUserPayload, mockBindingResult, mockSessionStatus); verify(mockUserPayload, times(2)).getUserSupplyTypesCodes(); assertThat(mav.getViewName(), equalTo("administration/partners/edit")); assertThat(mockUserPayload.getUserSupplyTypesCodes().size(), equalTo(1)); assertThat(mockUserPayload.getUserSupplyTypesCodes().iterator().next(), equalTo("Identity"));
verify(mockSessionStatus, never()).setComplete(); verify(mockCountryService, times(1)).getAllCountries(); verify(mockUserService, times(1)).getAllSupplyTypes();
} private void setupForRedisplayEditFormWhenError(boolean partnerTokenExists) { when(mockBindingResult.hasErrors()).thenReturn(true); when(mockUserPayload.getTokensDefined()).thenReturn(partnerTokenExists); when(mockUserPayload.getUserSupplyTypesCodes()).thenReturn(new HashSet<String>()); when(batchService.getAllByUser(anyString())).thenReturn(null); UserRole partnerRole = new UserRole("PTN1"); partnerRole.setIssuingUser(true); when(mockCurrencyManager.getUserRoles(any(String.class))).thenReturn(new UserRole());
Errors errors = new Errors(); when(mockValidationErrorsException.getViolations(any(ViolationSeverity.class))).thenReturn(errors); doThrow(mockValidationErrorsException).when(mockUserService).updateUser(any(UserPayload.class), any(URI.class));
}
38 .io
Be more accurate than this code ?
@RequestMapping(value = "/edit", method = RequestMethod.POST) public ModelAndView update(@RequestParam String resourceId, @ModelAttribute(PARTNER) UserPayload partner, BindingResult result, SessionStatus status) { boolean isTokenExists = partner.getTokensDefined(); if (partner.getUserSupplyTypesCodes() == null) partner.setUserSupplyTypesCodes(new HashSet<String>()); if (isTokenExists) partner.getUserSupplyTypesCodes().add("Identity");
UserRole partnerRole = currencyManager.getUserRoles(partner.getCode());
if (partnerRole.isIssuingUser()) partner.getUserSupplyTypesCodes().add("Issuance"); if (partnerRole.isLiabilityUser()) partner.getUserSupplyTypesCodes().add("Liability");
try { partnerService.updateUser(partner, URI.create(resourceId)); status.setComplete(); return new ModelAndView("redirect:/partners"); } catch (ValidationErrorsException e) { SpringErrorBindingHelper.populateErrors(result, e.getViolations(ViolationSeverity.ERROR)); ModelAndView mav = new ModelAndView("administration/partners/edit"); partnerRole = currencyManager.getUserRoles(partner.getCode()); mav.addObject(PARTNER_ROLE, partnerRole); mav.addObject(RESOURCE_ID, resourceId); populateReferenceData(mav); return mav; } }
39 .io
And this test?
"Sending two JSON records to the messages REST API" should {
"returns HTTP Status 200 and store two records to the messages repository" in {
// Given val jsonRows = Json.parse( """[ |{ "id": "1234567890", "form": "SA300", "dispatchOn": "2013-11-22", "detailsId": "W0123456781234569"}, |{ "id": "1234567891", "form": "SA316A", "dispatchOn": "2013-11-23", "detailsId": "C0123456781234568"} |]""".stripMargin)
// When verifyStatusCode(doPut(resource(s"/messages"), jsonRows), 200)
// Then val messages = await(messageRepository.findAll) messages should have size 2
exactly(1, messages) should have ( 'recipient("1234567890"), 'body("SA300", DetailsId("W0123456781234569")), 'dispatchOn(new LocalDate(2013, 11, 22)) )
exactly(1, messages) should have ( 'recipient("1234567891"), 'body("SA316A", DetailsId("C0123456781234568")), 'validFrom(new LocalDate(2013, 11, 23)) ) }}
40 .io
Compared to this code ?
def putMessages = Action.async(action.parser) { implicit request => withJsonBody[List[PrintSuppressionNotification]] { messages => if (messages == null || messages.isEmpty) throw new BadRequestException("No messages supplied") if (messages.size > maxMessages) throw new RequestEntityTooLargeException( s"${messages.size} items submitted, max $maxMessages allowed") val results = mongo.insertAllUnique(messages.map(_.toMongo)) Future.sequence(results).map(_ => Results.Ok) } }
41 .io
Other projects' testimonials
"Today however, my team told me the tests are more complex than the actual code. (This team is not the original team that wrote the code and unit tests. Therefore some unit tests take them by surprise. This current team is more senior and
disciplined.) In my opinion, now that’s waste..."
Richard Jacobs at Sogeti (Sogeti Nederland B.V.) http://www.rbcs-us.com/documents/Why-Most-Unit-Testing-is-
Waste.pdf
42 .io
Accurate = Small
More code =
more mistakes
Ideal size of a test? ~ 3 lines Assumption, Action, Assertion
"Less is more"[Ludwig Mies van der
Rohe]
43 .io
Accurate = Readable
We can trust what we understandMake your test easy to read
Anyone would understand and validate it
44 .io
Accurate = Explicit and repeatable
What You See is What Test DoesAgain and again the same
No side-effects No hidden logic in helper
No loopsNo random values
45 .io
Accurate = Traceable to a requirement
When test fails,what stops working?
Use business domain namesuse business stories names
for objects, actions and results
46 .io
Accurate = Correct when fails
If I comment out a line of code, does the test FAIL for the CORRECT reason?Does the test returns an error (exception)
instead of a failure?
47 .io
WHOOPS!MY TESTS ARE INACCURATE,
WHAT SHOULD I DO?
48 .io
49 .io
INACCURATE TESTIS WORSE THAN
NO TEST
50 .io
Are you telling me I should have less tests ?Q [John Nolan]:
"The thing I've found about TDD is that its takes time to get your tests set up and being naturally lazy I always want to write as little code as possible. The first thing I seem do is test my constructor has set all the properties but is this overkill?My question is to what level of granularity do you write you unit tests at?
..and is there a case of testing too much?"
A [Kent Beck]:
"I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence"
http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests/153565#153565
51 .io
REBUILDINGPROFESSIONAL TDD
https://www.flickr.com/photos/jdn/4817970175
52 .io
Experience over Rules
Writing CODE is MORE than applying RULES
Reading CODE is MORE than reading BOOKS
Common sense, Trial and Error
53 .io
The Sense of Time
Test too complex to write ? Kill the test (if the code is clean)
Test takes too long to understand? Kill the test (if the code is readable)
Time (with TDD) < Time (without TDD)or you are doing something wrong!
54 .io
Cost / value trade-off
Writing tests costs moneyTest marginal value > Test cost?
Is this code critical? Can it possibly fail? Am I double-testing something
covered?
Cost (with TDD) < Cost (without TDD)or you are doing something wrong!
55 .io
How big is my code-base with TDD?How many test / code lines I am
writing?Confidence on uncovered code?
Size (Test) < Size (Code)Do not test code that cannot break
Test / code ratio
56 .io
Test Focus
Tests express WHAT to achievefrom user's perspective
Test WHAT can be measured
What can I measure on this?public interface UserRepository { void addUser(String username, String first, String last);}
57 .io
Code confidence
Code (not Test) tells HOW system works
Code (with Test) gives confidence of correctness
We are confident on what is clear + readable + repeatable
58 .io
Writing code - Martin Fowler
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
[Martin Fowler - 1999 - Refactoring: Improving the Design of Existing Code]
59 .io
Best writers in history – Ernest Hemingway"In 1954, when Hemingway was awarded the Nobel Prize for Literature, it was for his mastery of the art of narrative […] He avoided complicated syntax. About 70 percent of the sentences are simple sentences—a childlike syntax without subordination."
http://en.wikipedia.org/wiki/Ernest_Hemingway
60 .io
Reading = learning to Write
Code ReviewRead A LOT of code
Read good code, read bad code
Comment, exchange ideasChallenge solutions
https://www.flickr.com/photos/jdn/4817970175
61 .io
Write, experiment, throw waste
Write code, ask for feedbackRead your own code …
after days, weeks, monthsMeasure progress
Throw wasteKeep test and code clean, ALWAYS
62 .io
Professional TDD Summary
1. TDD practices are not enough2. Experience over Rules3. Trade-off test time/cost and code benefit4. Tests is WHAT to achieve5. Code is confidence on DESIGN6. Read code to become a better writer7. Plan to throw test waste away
63 .io
64 .io
TDD resources for reading and learning Is TDD dead hangouts series
http://martinfowler.com/articles/is-tdd-dead/
Unit-testing and waste managementhttp://www.rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf
Mockist vs. non-mockist TDDhttp://martinfowler.com/articles/mocksArentStubs.html
65 .io
Code-review resources
Guido Van Rossum about Code Review @Googlehttp://www.youtube.com/watch?v=CKjRt48rZGk
Code Review @SAPhttp://www.eclipsecon.org/2013/sites/eclipsecon.org.2013/files/CodeReview.pptx
V. Subramaniam – About Code Review and Qualityhttp://www.agiledeveloper.com/presentations/caring_about_code_quality.pdf
Replay these slides:http://slideshare.net/lucamilanesio
Try Gerrit on-line: http://gerrithub.io/login
Learn Gerrit: http://gerrithub.io/book
Follow my Blog: http://gitenterprise.me
Learn more about code review
20% OFF Book discount for 33 Degree.org User Conference
Book CODE: dg7jnZ eBook CODE: Wi86Zh