• Save
Pitfalls Of Tdd Adoption by Bartosz Bankowski
Upcoming SlideShare
Loading in...5

Pitfalls Of Tdd Adoption by Bartosz Bankowski






Total Views
Views on SlideShare
Embed Views



8 Embeds 345

http://agileee.org 242
http://lib.custis.ru 62
http://team.custis.ru 26
http://static.slidesharecdn.com 5
http://www.slideshare.net 4
http://wiki.office.custis.ru 3
http://test.agileee.org 2
http://agileee.com 1



Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
Post Comment
Edit your comment

Pitfalls Of Tdd Adoption by Bartosz Bankowski Pitfalls Of Tdd Adoption by Bartosz Bankowski Presentation Transcript

  • Pitfalls of TDD Adoption Bartosz Baokowski
  • Agenda • Intro • What is TDD? • Why to adopt it? • Experience • Test smells and patterns • Summary • Where to go next?
  • About the author • Software developer • Agile coach/mentor • Open source contributor • Leader of Polish Agile User Group
  • What is TDD? • It seems everyone knows it nowadays. • At least everyone talks about it. • Well, why do we still have bad code?
  • Why to adopt it? • Explore system behavior. • Create a specification. • Drive code design. • Think about quality. • Last, but not least – build executable and automated tests.
  • Experience • TDD classes on major universities in Krakow, Poland since 2007. • TDD workshops at various conferences in Poland. • TDD coaching and trainings at Sabre Holdings.
  • The Good, The Bad & The Ugly aka what to do and what to avoid in your tests
  • What to test? • Methods vs Test Cases = 1:1 relationship • How to name a test method? class Flights { class FlightsTest { addFlight(...); testAddFlight(...); getFlight(...); testGetFlight(...); getFlightSeats(...); testGetFlightSeats(...); getCheapestSeat(...); testGetCheapestSeat(...); bookSeat(...); testBookSeat(...); } }
  • What to test? • Implicit constructor testing @Test public void testConstructor() { Flight flight = new Flight(); assertEquals(...); assertEquals(...); } @Test public void testConstructor() { Flight flight = new Flight(); assertNotNull(flight); }
  • It’s about behavior • ...so write examples: – tell me what your system does NOT have yet? – answer it in programming language – answer giving an example
  • Coding by example • User story: – As a user I want to get a number of available seats for a given flight class FlightTest { @Test public void shouldReturnNumberOfAvailableSeats() { // given Flight flight = new Flight(); flight.addSeats(10); // when int numberOfSeats = flight.getNumberOfAvailableSeats(); // then assertEquals(10, numberOfSeats); } }
  • Ultimate Test Template @Test public void should...() throws Exception { //given ... //when ... //then ... }
  • „Should” Reduce the chance tests will look like: But increase the chance for:
  • Comments //given ... //when ... //then ... Increase the chance the test is focused around single behavior.
  • Bloated fixture • One fixture to rule them all Test method 1 Fixture Test method 2 Test method 3 Test method 4
  • Healthy fixture • Extract only common parts to setup method @Test You want to avoid it. public void testAvailableSeatsCount() throws { assertEquals(0, f.getAvailableSeats()); f.addSeat(new Seat(1, 100)); assertEquals(1, f.getAvailableSeats()); } • Minimize it • Use creation methods where appropriate
  • Minimal fixture
  • Noisy fixture // given Flight flight1 = new Flight(1, new Market("KRK", "YVR"), new Date(), 50); Flight flight2 = new Flight(2, new Market("KRK", "YVR"), new Date(), 30); Flight flight3 = new Flight(3, new Market("HKG", "YVR"), new Date(), 60); List<Flight> flights = new ArrayList<Flight>(); flights.add(flight1); flights.add(flight2); flights.add(flight3); FlightManager flightManger = new FlightManager(flights); // when List<Flight> flights = flightManger .getFlightsFromOrigin("KRK"); // then assertEquals(2, flights.size()); assertTrue(flights.contains(flight1)); assertTrue(flights.contains(flight2));
  • Noisy fixture refactoring • Hide irrelevant data with creation method // given Flight flight1 = createFlightFromOrigin("KRK"); Flight flight2 = createFlightFromOrigin("KRK"); Flight flight3 = createFlightFromOrigin("HKG"); • Make it simple List<Flight> flights = new ArrayList<Flight>(); flights.add(flight1); flights.add(flight2); flights.add(flight3); FlightManager flightManger = new FlightManager(flights); FlightManager flightManager = new FlightManager(asList(flight1, flight2, flight3));
  • Fluent builders • Make code more readable • Worth to try if too many creation methods • Require time to build Flight flight = new Flight(1, new Market("KRK", "YVR"), new Date()); Flight flight = buildFlight().number(1).between("KRK", "YVR").today().build();
  • Stable test data • Golden rule: – No matter how many times you run the test it should always give the same results! • Random test data • Time & date tests can be tricky
  • One behavior per method • If you have more than one statement in when, you may need a new example. • If you have assertions outside then, you may need a new example.
  • One verification per test • Use assertions only in then block • Make them as expressive as possible assertEquals(2, flights.size()); assertTrue(flights.contains(flight1)); assertTrue(flights.contains(flight2)); Custom assertion: assertFlightsContainOnly(flights, flight1, flight2); Hamcerst: assertThat(flights, hasItems(flight1, flight2)); FEST-Assert: assertThat(flights).containsOnly(flight1, flight2);
  • Simplify assertions • You don’t want to guess what you are expecting. It’s expectation anyway! assertEquals((a + b + c) / 3, average); • Loops are bad. int pos = 0; for (ToolGroup group: groups) { assertTrue(pos <= group.getLocation()); pos = group.getLocation(); }
  • Simplify assertions • Never use conditionals! – You definately need a new example – ...or your example is broken
  • Testing exceptions • It seems fine: @Test(expected=IllegalFlightNumberException.class) public void shouldFailForWrongFlightNumber() throws Exception { FlightManager flightManager = new FlightManager(); flightManager.addFlight(new Flight(10)); flightManager.addFlight(new Flight(15)); flightManager.getFlight(50); } • But what if new Flight() can throw same exception?
  • Summary
  • How to make it work? • Train your people • Give them simple rules to adhere to (e.g. test template!) • Encourage pair programming • ...or at least code reviews • Do not listen to excuses – I don’t have time to write tests!
  • Where to go next? • Online: – xunitpatterns.com – behavior-driven.org • Offline: – „xUnit Test Patterns” by Gerard Meszaros – „Growing Object-Oriented Software, Guided by Tests” by Steve Freeman, Nat Pryce – „Behavior Driven Development in Ruby with RSpec” by David Chelimsky, Aslak Hellesoy