0
When TDD Goes Awry
Reggio Emilia - IAD 2013
Clueless tests, infesting
mocks and other horrors...
A voyage into today Java
...
SHO

to	
  start

SHIN	
  
Heart,	
  mind

In the beginner's mind there are many possibilities,
in the expert's mind there...
a·wry (-r)
adv.
1. In a position that is turned or twisted toward one
side; askew.
2. Away from the correct course; amiss.
a·wry (-r)
adv.
1. In a position that is turned or twisted toward one
side; askew.
2. Away from the correct course; amiss....
Test Stories
Each test should tell a story

Micro tests develop algorithms

Scenario tests illustrate the design

Test Stories
Each test should tell a story

Micro tests develop algorithms

Scenario tests illustrate the design


When yo...
Test Stories
Each test should tell a story

Micro tests develop algorithms

Scenario tests illustrate the design


When yo...
2001
My first project

2001
My first project

2001
Meaningful test names
12 years later… and still we are talking about TDD
12 years later… and still we are talking about TDD

Test Driven Design
12 years later… and still we are talking about TDD

Test Driven Design
It’s a kind of design technique, not a way to test.
12 years later… and still we are talking about TDD
I get paid for code that works, not for tests, so
my philosophy is to t...
12 years later… and still we are talking about TDD
I get paid for code that works, not for tests, so
my philosophy is to t...
Question:

Why designing for testability result
in good design?
The caveman house design

Carlo Pescio
Question:

Why designing for testability result
in good design?
The caveman house design

Carlo Pescio

Global state
Hidde...
@Test

public void testGetSwapType_SPOTFWD() {

when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");...
@Test

public void testGetSwapType_SPOTFWD() {

when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");...
Bad test smells: 1 - Too many assertions

Let’s start from Assertions
One of the least followed TDD rule says: “There must...
Bad test smells: 1 - Too many assertions

Let’s start from Assertions
One of the least followed TDD rule says: “There must...
Bad test smells: 1 - Too many assertions

Let’s start from Assertions
One of the least followed TDD rule says: “There must...
Multi Assertion I

@Test

public void splitInWords() throws Exception {




String text = "To be, or not to be: that is th...
Multi Assertion I

@Test

public void splitInWords() throws Exception {




String text = "To be, or not to be: that is th...
Multi Assertion II



@Test

public void calculateQuote() throws Exception {




Quote expected = new Quote("USDGBP", "0.6...
Multi Assertion III
@Test

public void commutativeProperty() throws Exception {




//these three should be put on 3 diffe...
Bad test smells: 2 - Too many doubles

Stubs
Mocks test behaviour, Stubs test state
!

Stubs doesn’t verify calls
!

Stubs...
Mocks
Mocks are complicated, try to use them scarcely.
!

Use stubs for decorators and close friends,
mocks for external c...
Smells: Assertions + Mocks
@Test

public void testGetSwapType_SPOTFWD() {

when(tradeBean.getField(TradeBeanFields.SWAP_TY...
Split and inline
@Test

public void testGetSwapType_SPOTFWD_SWP() {

when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).t...
Stubs with builders
@Test

public void useSwapTypeIfTradingIsSwap() {

//split test in two

//construct concrete bean usin...
High Coupled Design
@Test

public void testBusSourceSendMessage() throws Exception {




//complex setup, unclear design, ...
Bad test smells: 3 - High Coupling

High Coupling

!
!
!
!
!
In software engineering, coupling or dependency is the degree...
The usual culprit:
Dependency Injection frameworks
The best classes in any application are the ones that do stuff: the
Bar...
I beg to differ, 

duct tape is important!
I beg to differ, 

duct tape is important!
That is, it’s important to
wiring up our objects 

in the best possible way.
!
...
High Coupled
@Test

public void testBusSourceSendMessage() throws Exception {




//complex setup, unclear design, high co...
Unmock it with a simpler object
@Test

public void simplifiedBusSourceSendMessage() throws Exception {

//simplify the rea...
Unmock it with a simpler object
@Test

public void simplifiedBusSourceSendMessage() throws Exception {

//simplify the rea...
Test with a listener mock
@Test

public void whenUpdateSendMessageToListeners() throws Exception {




Quote eurusd = new ...
Testing Layers
@Test

public void retrieveModules() throws Exception {

Page page = new Page();

Repository repo = mock(Re...
Lasagna Code

There is no problem in computer science that cannot be
solved by adding another layer of indirection, except...
Bad test smells: 4 - Complicated Tests
Bad test smells: 4 - Complicated Tests

We have a problem, 

Our code is too difficult to test
Bad test smells: 4 - Complicated Tests

We have a problem, 

Our code is too difficult to test
Let's write a framework to t...
Bad test smells: 4 - Complicated Tests

We have a problem, 

Our code is too difficult to test
Let's write a framework to t...
Bad test smells: 4 - Complicated Tests

We have a problem, 

Our code is too difficult to test
Let's write a framework to t...
Bad test smells: 4 - Complicated Tests

We have a problem, 

Our code is too difficult to test
Let's write a framework to t...
Testing Layers separately
@Test

public void retrieveModules() throws Exception {

Page page = new Page();

Repository rep...
Testing Layers together
@Test

public void composePage() throws Exception {

//we can use a single test here from xml to j...
How to improve
How to improve
If your tests give you pain don't ignore it. Localise
the cause.
How to improve
If your tests give you pain don't ignore it. Localise
the cause.
How to improve
If your tests give you pain don't ignore it. Localise
the cause.
Ask to new team members or dev from other
...
How to improve
If your tests give you pain don't ignore it. Localise
the cause.
Ask to new team members or dev from other
...
How to improve
If your tests give you pain don't ignore it. Localise
the cause.
Ask to new team members or dev from other
...
How to improve
If your tests give you pain don't ignore it. Localise
the cause.
Ask to new team members or dev from other
...
How to improve
If your tests give you pain don't ignore it. Localise
the cause.
Ask to new team members or dev from other
...
Upcoming SlideShare
Loading in...5
×

When Tdd Goes Awry (IAD 2013)

3,132

Published on

Published in: Technology, Business

Transcript of "When Tdd Goes Awry (IAD 2013)"

  1. 1. When TDD Goes Awry Reggio Emilia - IAD 2013 Clueless tests, infesting mocks and other horrors... A voyage into today Java Enterprise worse practices. Uberto  Barbini
 @ramtop
 h0ps://github.com/uberto
  2. 2. SHO
 to  start SHIN   Heart,  mind In the beginner's mind there are many possibilities, in the expert's mind there are few.
  3. 3. a·wry (-r) adv. 1. In a position that is turned or twisted toward one side; askew. 2. Away from the correct course; amiss.
  4. 4. a·wry (-r) adv. 1. In a position that is turned or twisted toward one side; askew. 2. Away from the correct course; amiss. @Test
 public void testGetSwapType_SPOTFWD() {
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 setUpTrade("SWAP");
 assertEquals(TradeType.SPOTFWD, trade.getType());
 
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 setUpTrade("FWDFWDSWAP");
 assertEquals(TradeType.SPOTFWD, trade.getType());
 }
 
 
 
 private void setUpTrade(final String tradingType) {
 when(tradeBean.getField(TradeBeanFields.ACCOUNT)).thenReturn(ACCOUNT_VAL);
 when(tradeBean.getField(TradeBeanFields.PRICE)).thenReturn(PRICE);
 when(tradeBean.getField(TradeBeanFields.TRADING_TYPE)).thenReturn(tradingType);
 trade = new Trade(tradeBean);
 }
  5. 5. Test Stories Each test should tell a story
 Micro tests develop algorithms
 Scenario tests illustrate the design

  6. 6. Test Stories Each test should tell a story
 Micro tests develop algorithms
 Scenario tests illustrate the design
 When you are thinking big thoughts, write big tests. When you are thinking little thoughts, write little tests. ! Kent Beck, Quora
  7. 7. Test Stories Each test should tell a story
 Micro tests develop algorithms
 Scenario tests illustrate the design
 When you are thinking big thoughts, write big tests. When you are thinking little thoughts, write little tests. ! Kent Beck, Quora Objects are nouns. Methods are verb. Good design is a good story. A good test remind us of a good design. 
 Do you remember XP Metaphor?
  8. 8. 2001
  9. 9. My first project 2001
  10. 10. My first project 2001 Meaningful test names
  11. 11. 12 years later… and still we are talking about TDD
  12. 12. 12 years later… and still we are talking about TDD Test Driven Design
  13. 13. 12 years later… and still we are talking about TDD Test Driven Design It’s a kind of design technique, not a way to test.
  14. 14. 12 years later… and still we are talking about TDD 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.
 
 Kent Beck Stackoverflow Test Driven Design It’s a kind of design technique, not a way to test.
  15. 15. 12 years later… and still we are talking about TDD 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.
 
 Kent Beck Stackoverflow Test Driven Design It’s a kind of design technique, not a way to test. When TDD is not useful: when your don’t care about design
 i.e.. technical spikes, learning exercises
  16. 16. Question:
 Why designing for testability result in good design? The caveman house design
 Carlo Pescio
  17. 17. Question:
 Why designing for testability result in good design? The caveman house design
 Carlo Pescio Global state Hidden dependencies Inflexible behaviour Things that work together are kept close

  18. 18. @Test
 public void testGetSwapType_SPOTFWD() {
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 setUpTrade("SWAP");
 assertEquals(TradeType.SPOTFWD, trade.getType());
 
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 setUpTrade("FWDFWDSWAP");
 assertEquals(TradeType.SPOTFWD, trade.getType());
 }
 
 
 
 private void setUpTrade(final String tradingType) {
 when(tradeBean.getField(TradeBeanFields.ACCOUNT)).thenReturn(ACCOUNT_VAL);
 when(tradeBean.getField(TradeBeanFields.PRICE)).thenReturn(PRICE);
 when(tradeBean.getField(TradeBeanFields.TRADING_TYPE)).thenReturn(tradingType);
 trade = new Trade(tradeBean);
 }
  19. 19. @Test
 public void testGetSwapType_SPOTFWD() {
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 setUpTrade("SWAP");
 assertEquals(TradeType.SPOTFWD, trade.getType());
 
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 setUpTrade("FWDFWDSWAP");
 assertEquals(TradeType.SPOTFWD, trade.getType());
 }
 
 
 
 private void setUpTrade(final String tradingType) {
 when(tradeBean.getField(TradeBeanFields.ACCOUNT)).thenReturn(ACCOUNT_VAL);
 when(tradeBean.getField(TradeBeanFields.PRICE)).thenReturn(PRICE);
 when(tradeBean.getField(TradeBeanFields.TRADING_TYPE)).thenReturn(tradingType);
 trade = new Trade(tradeBean);
 } If to test 3 lines of simple code, we have 10 lines of complicated test…
 ! Which is more likely to have a bug? the code or the test?
  20. 20. Bad test smells: 1 - Too many assertions Let’s start from Assertions One of the least followed TDD rule says: “There must be one assertion for test”. Why?
  21. 21. Bad test smells: 1 - Too many assertions Let’s start from Assertions One of the least followed TDD rule says: “There must be one assertion for test”. Why? The point behind testing one thing at time is the we want to run all the state checks, every time, independently. ! No logic in the tests, not even an “if”. ! 
 As less as possible duplication with the logic being tested, ideally no duplication with other tests.

  22. 22. Bad test smells: 1 - Too many assertions Let’s start from Assertions One of the least followed TDD rule says: “There must be one assertion for test”. Why? The point behind testing one thing at time is the we want to run all the state checks, every time, independently. ! No logic in the tests, not even an “if”. ! As less as possible duplication with the logic being tested, ideally no duplication with other tests.
 
 There are 3 kinds of multiple assertions:
  23. 23. Multi Assertion I @Test
 public void splitInWords() throws Exception {
 
 String text = "To be, or not to be: that is the question";
 String[] words = getWords(text);
 
 //these assertion are increasing the precision, 
 //if one fails it makes no sense to run the next
 assertNotNull(words);
 
 assertTrue(words.length > 0);
 assertEquals("To", words[0]);
 
 assertEquals(10, words.length);
 assertEquals("question", words[9]);
 
 }
 private String[] getWords(String s) {
 return s.split(" ");
 }
  24. 24. Multi Assertion I @Test
 public void splitInWords() throws Exception {
 
 String text = "To be, or not to be: that is the question";
 String[] words = getWords(text);
 
 //these assertion are increasing the precision, 
 //if one fails it makes no sense to run the next
 assertNotNull(words);
 
 assertTrue(words.length > 0);
 assertEquals("To", words[0]);
 
 assertEquals(10, words.length);
 assertEquals("question", words[9]);
 }
 
 private String[] getWords(String s) {
 return s.split(" ");
 } Sometimes scenario tests use assertions in this way
  25. 25. Multi Assertion II 
 @Test
 public void calculateQuote() throws Exception {
 
 Quote expected = new Quote("USDGBP", "0.62");
 
 Quote calculated = getQuote("USD", "GBP");
 
 //no very good because if one fail we don't know the value of the other check
 assertEquals(expected.getSubject(), calculated.getSubject());
 assertEquals(expected.getValue(), calculated.getValue());
 //much better because it compare all fields every time
 assertThat(expected, sameQuote(calculated));
 }
 
 
 private Matcher<Quote> sameQuote(Quote quote) {
 return new QuoteMatcher(quote);
 }
 
 private Quote getQuote(String cur1, String cur2) {
 return new Quote(cur1+cur2, "0.62");
 }
  26. 26. Multi Assertion III @Test
 public void commutativeProperty() throws Exception {
 
 //these three should be put on 3 different tests
 //or use some kind of data table assertions like spec
 //or maybe assertAndContinue()
 
 
 assertEquals(5, sum(3,2));
 assertEquals(8, sum(3,5));
 assertEquals(sum(3,sum(4,5)), sum(sum(3, 4),5));
 }

  27. 27. Bad test smells: 2 - Too many doubles Stubs Mocks test behaviour, Stubs test state ! Stubs doesn’t verify calls ! Stubs doesn’t check for parameters ! Stubs can be easily reused. Different examples. ! No need to create stubs dynamically. ! No need to use doubles at all for immutables.
  28. 28. Mocks Mocks are complicated, try to use them scarcely. ! Use stubs for decorators and close friends, mocks for external collaborators (i.e. listeners) ! Stubs can be prepared in setup. Mocks must be “loaded” in the actual test. ! Try to verify mocks with actual params or matcher, not any (or maybe you wanted a stub instead?).
  29. 29. Smells: Assertions + Mocks @Test
 public void testGetSwapType_SPOTFWD() {
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 setUpTrade("SWAP");
 assertEquals(TradeType.SPOTFWD, trade.getType());
 
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 setUpTrade("FWDFWDSWAP");
 assertEquals(TradeType.SPOTFWD, trade.getType());
 }
 
 
 
 private void setUpTrade(final String tradingType) {
 when(tradeBean.getField(TradeBeanFields.ACCOUNT)).thenReturn(ACCOUNT_VAL);
 when(tradeBean.getField(TradeBeanFields.PRICE)).thenReturn(PRICE);
 when(tradeBean.getField(TradeBeanFields.TRADING_TYPE)).thenReturn(tradingType);
 trade = new Trade(tradeBean);
 }
  30. 30. Split and inline @Test
 public void testGetSwapType_SPOTFWD_SWP() {
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 when(tradeBean.getField(TradeBeanFields.ACCOUNT)).thenReturn(ACCOUNT_VAL);
 when(tradeBean.getField(TradeBeanFields.PRICE)).thenReturn(PRICE);
 when(tradeBean.getField(TradeBeanFields.TRADING_TYPE)).thenReturn("SWAP");
 trade = new Trade(tradeBean);
 assertEquals(TradeType.SPOTFWD, trade.getType());
 }
 
 @Test
 public void testGetSwapType_SPOTFWD_FWDSWP() {
 when(tradeBean.getField(TradeBeanFields.SWAP_TYPE)).thenReturn("SPOTFWD");
 when(tradeBean.getField(TradeBeanFields.ACCOUNT)).thenReturn(ACCOUNT_VAL);
 when(tradeBean.getField(TradeBeanFields.PRICE)).thenReturn(PRICE);
 when(tradeBean.getField(TradeBeanFields.TRADING_TYPE)).thenReturn("FWDFWDSWAP");
 trade = new Trade(tradeBean);
 assertEquals(TradeType.SPOTFWD, trade.getType());
 } !
  31. 31. Stubs with builders @Test
 public void useSwapTypeIfTradingIsSwap() {
 //split test in two
 //construct concrete bean using a fluent builder
 tradeBean = SimpleTradeBean.prepare().currencies("EURGBP", "0.67").swapType("SPOTFWD").tradingType("SWAP");
 Bean is mutable 
 trade = new Trade(tradeBean);
 
 //next step: better using a matcher on an expected Trade rather than 
 // compare a field at time
 assertEquals(TradeType.SPOTFWD, trade.getType());
 }
 
 @Test
 public void useSwapTypeIfTradingIsFwdSwap() {
 tradeBean = SimpleTradeBean.prepare().currencies("USDGBP", "1.2").swapType("SPOTFWD").tradingType("FWDSWAP");
 
 trade = new Trade(tradeBean);
 
 
 assertEquals(TradeType.SPOTFWD, trade.getType());
 }
  32. 32. High Coupled Design @Test
 public void testBusSourceSendMessage() throws Exception {
 
 //complex setup, unclear design, high coupling
 when(mySource.createActivePublisher(any(String.class), any(DataFetcher.class))).thenReturn(myPub);
 when(myPub.getMessageFactory()).thenReturn(myMsgFactory);
 when(myMsgFactory.createMessage("EURUSD")).thenReturn(myMsg);
 
 myFetcher = new DataFetcher(mySource);
 
 mySource.createActivePublisher("quotes", myFetcher);
 
 //why mocking a immutable object?
 MessageItem item = mock(MessageItem.class);
 
 when(item.getSubject()).thenReturn("EURUSD");
 
 //7 lines of mocks to test 3 lines of code
 myFetcher.updateData(item);
 
 //hard to understand the goal of this test from the verify
 verify(mySource).notify(myMsg);
 } !
  33. 33. Bad test smells: 3 - High Coupling High Coupling ! ! ! ! ! In software engineering, coupling or dependency is the degree to which each program module relies on each one of the other modules. antipattern of high coupling: ! cohesion refers to the degree to which the elements of a module belong together.[1] Thus, it is a measure of how strongly-related each piece of functionality expressed by the source code of a software module is. 
 
 
 Wikipedia
  34. 34. The usual culprit: Dependency Injection frameworks The best classes in any application are the ones that do stuff: the BarcodeDecoder, the KoopaPhysicsEngine, and theAudioStreamer. These classes have dependencies; perhaps a BarcodeCameraFinder, DefaultPhysicsEngine, and an HttpStreamer.! To contrast, the worst classes in any application are the ones that take up space without doing much at all: theBarcodeDecoderFactory, the CameraServiceLoader, and the MutableContextWrapper. These classes are the clumsy duct tape that wires the interesting stuff together.! Dagger is a replacement for these FactoryFactory classes. It allows you to focus on the interesting classes. Declare dependencies, specify how to satisfy them, and ship your app.! ! from Dagger introduction! http://square.github.io/dagger/ Good things about Dagger: 
 good and invisible duct tape
  35. 35. I beg to differ, 
 duct tape is important!
  36. 36. I beg to differ, 
 duct tape is important! That is, it’s important to wiring up our objects 
 in the best possible way. ! Write tests to show how your wiring is done ! Replace Duct Tape with Demeter
  37. 37. High Coupled @Test
 public void testBusSourceSendMessage() throws Exception {
 
 //complex setup, unclear design, high coupling
 when(mySource.createActivePublisher(any(String.class), any(DataFetcher.class))).thenReturn(myPub);
 when(myPub.getMessageFactory()).thenReturn(myMsgFactory);
 when(myMsgFactory.createMessage("EURUSD")).thenReturn(myMsg);
 
 myFetcher = new DataFetcher(mySource);
 
 mySource.createActivePublisher("quotes", myFetcher);
 
 //why mocking a immutable object?
 MessageItem item = mock(MessageItem.class);
 
 when(item.getSubject()).thenReturn("EURUSD");
 
 //7 lines of mocks to test 3 lines of code
 myFetcher.updateData(item);
 
 //hard to understand the goal of this test from the verify
 verify(mySource).notify(myMsg);
 } !
  38. 38. Unmock it with a simpler object @Test
 public void simplifiedBusSourceSendMessage() throws Exception {
 //simplify the real class to use it in tests
 BusSource mySource = new SimpleBusSource();
 
 myFetcher = new DataFetcher(mySource);
 
 Publisher myPub = mySource.createActivePublisher("quotes", myFetcher);
 
 
 
 // myFetcher.updateData(item);
 //verify(mySource).notify(myMsg);
 //we cannot yet verify the fetcher, so we copied fetcher code here and test it
 myMsg = myPub.getMessageFactory().createMessage("EURGBP");
 assertEquals("EURGBP", myMsg.getId());
 
 } ! !
  39. 39. Unmock it with a simpler object @Test
 public void simplifiedBusSourceSendMessage() throws Exception {
 //simplify the real class to use it in tests
 BusSource mySource = new SimpleBusSource();
 
 myFetcher = new DataFetcher(mySource);
 
 Publisher myPub = mySource.createActivePublisher("quotes", myFetcher);
 
 
 
 // myFetcher.updateData(item);
 //verify(mySource).notify(myMsg);
 //we cannot yet verify the fetcher, so we copied fetcher code here and test it
 myMsg = myPub.getMessageFactory().createMessage("EURGBP");
 assertEquals("EURGBP", myMsg.getId());
 
 } ! !
  40. 40. Test with a listener mock @Test
 public void whenUpdateSendMessageToListeners() throws Exception {
 
 Quote eurusd = new Quote("EURUSD", "1.2");
 
 //Simplified BusSource. More complex versions can exist for reporting, etc.
 mySource = new SimpleBusSource();
 
 myFetcher = new DataFetcher(mySource);
 
 //let's register a listener for all topics
 //this is the only mock, at the end of the chain of real objects
 // working together
 mySource.addTopicListener("*", myListener);
 
 //when there is an update on data...
 myFetcher.updateData(eurusd);
 
 //we check same data arrives to interested listeners
 verify(myListener).refresh(argThat(new SamePayload(eurusd)) );
 }
  41. 41. Testing Layers @Test
 public void retrieveModules() throws Exception {
 Page page = new Page();
 Repository repo = mock(Repository.class);
 UserData context = new UserData("gb");
 
 //first let's get the page layout for the user country in a parsed xml
 MapOfString pageDescriptor = repo.getPageDescriptor(context.getCountry()); 
 //then get the id of actual modules needed matching user context with page layout
 List<String> moduleIds = page.selectModules(context, pageDescriptor);
 
 //get the modules as string properties from parsed xml
 List<MapOfString> modules = repo.getModules(moduleIds);
 
 //to be safe we need to make sure we are using same stub in this test and the next
 assertEquals(STUB_MODULES, modules);
 }
 @Test
 public void composePage() throws Exception {
 //here we continue the flow from the previous test
 Page page = new Page();
 
 //get the modules as string properties from parsed xml
 List<MapOfString> modules = STUB_MODULES;
 
 //compose json page from properties
 String jsonPage = page.compose(modules);
 
 assertEquals(expectedJson, jsonPage);
 }

  42. 42. Lasagna Code There is no problem in computer science that cannot be solved by adding another layer of indirection, except having too many layers of indirection
  43. 43. Bad test smells: 4 - Complicated Tests
  44. 44. Bad test smells: 4 - Complicated Tests We have a problem, 
 Our code is too difficult to test
  45. 45. Bad test smells: 4 - Complicated Tests We have a problem, 
 Our code is too difficult to test Let's write a framework to test it!
  46. 46. Bad test smells: 4 - Complicated Tests We have a problem, 
 Our code is too difficult to test Let's write a framework to test it! Ok, now we have 2 problems...
  47. 47. Bad test smells: 4 - Complicated Tests We have a problem, 
 Our code is too difficult to test Let's write a framework to test it! Ok, now we have 2 problems... Dedicated test stub must be simple and transparent.
 They should explain the model, not hide it.
  48. 48. Bad test smells: 4 - Complicated Tests We have a problem, 
 Our code is too difficult to test Let's write a framework to test it! Ok, now we have 2 problems... Same problem for who has to develop against a big framework: even if I have the framework tests, how can I be sure of not losing pieces around? Let's model domain simply as whole and then split it up for the framework. Dedicated test stub must be simple and transparent.
 They should explain the model, not hide it.
  49. 49. Testing Layers separately @Test
 public void retrieveModules() throws Exception {
 Page page = new Page();
 Repository repo = mock(Repository.class);
 UserData context = new UserData("gb");
 
 //first let's get the page layout for the user country in a parsed xml
 MapOfString pageDescriptor = repo.getPageDescriptor(context.getCountry()); 
 //then get the id of actual modules needed matching user context with page layout
 List<String> moduleIds = page.selectModules(context, pageDescriptor);
 
 //get the modules as string properties from parsed xml
 List<MapOfString> modules = repo.getModules(moduleIds);
 
 //to be safe we need to make sure we are using same stub in this test and the next
 assertEquals(STUB_MODULES, modules);
 }
 @Test
 public void composePage() throws Exception {
 //here we continue the flow from the previous test
 Page page = new Page();
 
 //get the modules as string properties from parsed xml
 List<MapOfString> modules = STUB_MODULES;
 
 //compose json page from properties
 String jsonPage = page.compose(modules);
 
 assertEquals(expectedJson, jsonPage);
 }

  50. 50. Testing Layers together @Test
 public void composePage() throws Exception {
 //we can use a single test here from xml to json
 
 //only mock Repository, because implementation is in another sub-project
 Repository repo = mock(Repository.class);
 when(repo.getLayoutPage(COUNTRY)).thenReturn(LAYOUT_XML);
 when(repo.getModules(MODULE_IDS)).thenReturn(MODULES_XML);
 
 UserData context = new UserData(COUNTRY);
 
 //instead of MapOfStrings we use a proper object to keep layout
 Layout page = Layout.buildFromXml(repo.getLayoutPage(context.getCountry()));
 
 //we use Module object to keep module properties and methods
 List<Module> moduleList = page.prepareModules(repo, context);
 
 //render the list of modules, easier than with strings properties
 String jsonPage = renderer.toJson(moduleList);
 
 //checking matcher
 assertThat(EXPECTED_JSON, sameJson(jsonPage));
 }
  51. 51. How to improve
  52. 52. How to improve If your tests give you pain don't ignore it. Localise the cause.
  53. 53. How to improve If your tests give you pain don't ignore it. Localise the cause.
  54. 54. How to improve If your tests give you pain don't ignore it. Localise the cause. Ask to new team members or dev from other teams their impressions.
  55. 55. How to improve If your tests give you pain don't ignore it. Localise the cause. Ask to new team members or dev from other teams their impressions.
  56. 56. How to improve If your tests give you pain don't ignore it. Localise the cause. Ask to new team members or dev from other teams their impressions. Experiment and share.
  57. 57. How to improve If your tests give you pain don't ignore it. Localise the cause. Ask to new team members or dev from other teams their impressions. Experiment and share.
  58. 58. How to improve If your tests give you pain don't ignore it. Localise the cause. Ask to new team members or dev from other teams their impressions. Experiment and share. Rule 0: TDD is supposed to be fun and simple.
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×