40
Test Driven Development Primer Jim Gough twitter: @JavaJimLondon email: [email protected] aptood: http://aptood.com/6f1a22

Test Driven Development Primer LJC Open Conference

Embed Size (px)

DESCRIPTION

Sometimes getting started with Test Driven Development is a difficult change for people to pick up. This presentation gives a brief guide to how to get started and suggests a few examples and practical points. The examples are a bit heavy in terms of they include some Spring - something I'll be looking to improve in the future

Citation preview

Page 1: Test Driven Development Primer LJC Open Conference

Test Driven Development Primer

!Jim Gough

!twitter: @JavaJimLondon email: [email protected]

aptood: http://aptood.com/6f1a22

Page 2: Test Driven Development Primer LJC Open Conference

Schedule

Introduction

Cognitive implications

Mocking

Legacy code

Page 3: Test Driven Development Primer LJC Open Conference

Who am I?

6 years developing in finance.

London Java Community/JCP Panel.

Strong belief in craftsmanship.

Page 4: Test Driven Development Primer LJC Open Conference

Disclaimer

Based on personal experience.

Based on what others found difficult.

I probably take some shortcuts.

I can definitely improve.

Page 5: Test Driven Development Primer LJC Open Conference

What is Test Driven Development?

Write a test first.

Make sure it compiles, runs and fails.

Write the simplest code to pass the test.

Refactor the code and if necessary the test.

Page 6: Test Driven Development Primer LJC Open Conference

http://www.flickr.com/photos/nocallerid_man/3638360458/

Page 7: Test Driven Development Primer LJC Open Conference

Cognitive Implications

http://www.flickr.com/photos/aloha75/4571410233

Page 8: Test Driven Development Primer LJC Open Conference

Cognitive Implications

http://www.flickr.com/photos/eepaul/3946701733/

Page 9: Test Driven Development Primer LJC Open Conference

Cognitive ImplicationsCompose.

Comprehend.

Vary depending on maintenance vs new development.

Rapid increase in complexity from the offset.

Page 10: Test Driven Development Primer LJC Open Conference

Jigsaws

Page 11: Test Driven Development Primer LJC Open Conference

Get the tech right

Page 12: Test Driven Development Primer LJC Open Conference
Page 13: Test Driven Development Primer LJC Open Conference

One Approach

Controller/ UI End Point

Mock

Handler Logic

Mock

External Dependencies

Order Controller

Bartender Database

Page 14: Test Driven Development Primer LJC Open Conference

Difficult ChangeReverse the way the way that you think.

Start from the interactions and the spec.

Asking questions.

Exploring the domain.

Answer the question with solutions.

Take a step further in.

Page 15: Test Driven Development Primer LJC Open Conference

Mocking

Ensure we only test one unit of code at a time. !

!

Helps us keep our goal of thinking about each part in stages.

http://www.flickr.com/photos/ick9s/3950547398/

Page 16: Test Driven Development Primer LJC Open Conference

Verifying Interaction@RunWith(MockitoJUnitRunner.class)public class TestOrderController {

@Mock private BartenderService bartender;

private OrderController orderController;

@Before public void before() { orderController = new OrderController(); orderController. setOrderService(bartender);

Page 17: Test Driven Development Primer LJC Open Conference

Verifying Interaction@Testpublic void testOrderBeer() {

orderController.order(Drink.BEER);

verify(bartender).obtain(Drink.BEER);!}

Page 18: Test Driven Development Primer LJC Open Conference

Verifying Interaction@Testpublic void testOrderTwoDrinks() {

Drink[] drinks = new Drink[] { Drink.BEER, Drink.WINE };! orderController.order(drinks);

verify(bartender, times(2)) .obtain(any(Drink.class));!}

Page 19: Test Driven Development Primer LJC Open Conference

Verifying Interaction@Testpublic void testOrderTwoDrinks() {

Drink[] drinks = new Drink[] { Drink.BEER, Drink.WINE };! orderController.order(drinks);

verify(bartender).obtain(Drink.BEER);verify(bartender).obtain(Drink.WINE);

verifyNoMoreInteractions(bartender);!}

Page 20: Test Driven Development Primer LJC Open Conference

Stubbing

when(databaseBean.inStock(Drink.BEER)) .thenReturn(true);

when(databaseBean.inStock(Drink.BEER)) .thenReturn(true)

.thenReturn(false);

when(databaseBean.inStock(any(Drink.class)) .thenReturn(true)

.thenReturn(false);

Page 21: Test Driven Development Primer LJC Open Conference

StubbingdoThrow(new RuntimeException()).when(databaseBean).removeStock();when(namedParameterJdbcTemplate.update(eq(Employer.INSERT_EMPLOYER), any(SqlParameterSource.class), any(GeneratedKeyHolder.class), any(String[].class))).then(new Answer<Integer>() {! @Override public Integer answer(InvocationOnMock invocation) throws Throwable { //Do things with arguments

// Return }});

Page 22: Test Driven Development Primer LJC Open Conference

Customer wants to order an ale.

Page 23: Test Driven Development Primer LJC Open Conference

@Test public voidcustomer_orders_an_ale() { //Initialise handlerAdapter

private AnnotationMethodHandlerAdapter handlerAdapter;protected MockHttpServletResponse response;

MockHttpServletRequest request = new MockHttpServletRequest();request.setURI(“/order/ale”);request.setMethod(RequestMethod.POST);

handlerAdapter.handle(request, response, orderController);verify(bartender).orderAle(ale);

Ale ale = new Ale(“IPA”);request.setContent(mapper.writeValueAsBytes( ale));

Page 24: Test Driven Development Primer LJC Open Conference

private Bartender bartender;

@RequestMapping(value="order/ale", method=RequestMethod.POST)

@ResponseStatus(value=HttpStatus.OK)

public Drink orderAle(@RequestBody Ale ale) { return bartender.order(ale);!!!}

@RequestBody

Page 25: Test Driven Development Primer LJC Open Conference

@Test public voidpour_ale_that_is_available() {}

@Test public voidale_not_available() {}

Page 26: Test Driven Development Primer LJC Open Conference

@Mockprivate JdbcTemplate jdbcTemplate;

@Test public voidpour_ale_that_is_available() { Object[] params = new Object[1]; params[0] = “IPA”; when(jdbcTemplate.query(“SELECT * FROM Stock WHERE NAME = ?”, params, any(BeerMapper.class)).thenReturn(new Drink(“IPA”));

@InjectMocksprivate Bartender bartender;

Drink drink = bartender.order(ale);verify(jdbcTemplate).query(anyString(), any(Object[].class), any(BeerMapper.class));verify(jdbcTemplate).update(REMOVE_DRINK, params);assertThat(drink.getName(), isEqualTo(“IPA”));

Page 27: Test Driven Development Primer LJC Open Conference

@Mockprivate JdbcTemplate jdbcTemplate;

@Test public voidale_not_available() { when(jdbcTemplate.query(“SELECT * FROM Stock WHERE NAME = ?”, any(Object[].class), any(BeerMapper.class)) .thenReturn(null);

@InjectMocksprivate Bartender bartender;

Drink drink = bartender.order(ale);verify(jdbcTemplate).query(anyString(), any(Object[].class), any(BeerMapper.class));

assertNull(drink);

Page 28: Test Driven Development Primer LJC Open Conference

public Drink order(Ale ale) {

private JdbcTemplate jdbcTemplate;

Object[] params = new Object[] { ale.getName() };List<Drink> drinks = jdbcTemplate.query(SELECT_DRINK, params, beerMapper);if(drinks.size() > 0) { jdbcTemplate.update(REMOVE_DRINK, params); return drinks.get(0);} else { return null;}

Page 29: Test Driven Development Primer LJC Open Conference

Refactoring Legacy Code

Page 30: Test Driven Development Primer LJC Open Conference

Legacy Code

Often following push and pray mentality.

Lots of regression or hands on testing.

Developer time is often wasted.

Highly coupled code, changes become risker over time.

Team culture can be of a fixed way.

Page 31: Test Driven Development Primer LJC Open Conference

Can I Use TDD?

Absolutely, but there are a few new techniques to learn.

Don’t change production code without a test.

Page 32: Test Driven Development Primer LJC Open Conference

One Approach

Write tests to cover production code.

Only refactor allowed are automated IDE steps.

Break statics using intermediate step by identifying seams.

Page 33: Test Driven Development Primer LJC Open Conference

Dealing with Seams

public class MyLegacyClass {

public String getTimesLoggedIn() { User user = UserSession.getInstance() .getLoggedInUser(); return user.timesLoggedIn(); }}

Page 34: Test Driven Development Primer LJC Open Conference

Dealing with Seams

public class MyLegacyClass {

public String getTimesLoggedIn() { User user = UserSession.getInstance() .getLoggedInUser(); return user.timesLoggedIn(); }

protected User getLoggedInUser() { return UserSession.getInstance() .getLoggedInUser();!}

Page 35: Test Driven Development Primer LJC Open Conference

Dealing with Seams

public class MyLegacyClass {

public String getTimesLoggedIn() { User user = getLoggedInUser(); return user.timesLoggedIn(); }

protected User getLoggedInUser() { return UserSession.getInstance() .getLoggedInUser();!}

Page 36: Test Driven Development Primer LJC Open Conference

Dealing with Seams

public class MyLegacyClassTest {

private class TestableMyLegacyClass extends MyLegacyClass { !!!!}

@Overrideprotected User getLoggedInUser() { return loggedInUser;!}

private User loggedInUser;

Page 37: Test Driven Development Primer LJC Open Conference

Recommended Tutorial

http://www.youtube.com/watch?v=_NnElPO5BU0

Page 38: Test Driven Development Primer LJC Open Conference

BenefitsExplore the domain, comprehend the problem.

Start with meaningful requirements.

Framework to solve complicated problems.

High confidence in changes.

Eventually, quicker more productive and accurate.

Sanity.

Page 39: Test Driven Development Primer LJC Open Conference

ChallengesInitially you have to reverse way of thinking.

You sometimes have to sell it to team members.

Sell the benefits to management.

Less risk changes:

One project, 5-6 issues per week - now 5 issues per year.

Developer productivity goes up.

Page 40: Test Driven Development Primer LJC Open Conference

The End Get ready to test!

!Jim Gough

!twitter: @JavaJimLondon email: [email protected]

aptood: http://aptood.com/6f1a22