mock *Advanced topic. Beware over-mocking. • In-depth discussion of top-down vs bottom-up TDD. • Too much discussion about design benefits of listening to your tests. • Deep discussion of refactoring.
the simplest code possible to Make the test pass 4. Refactor: improve the design without changing behavior (keeping all tests passing) 1. Think about what you want to implement next
we get to a known state with the new functionality. You can commit any number of sins to get there, because speed trumps design, just for that brief moment. To quote Kent Beck...
5.0, 2.5)), equalTo(3.0)); } public class StatsCalculator { public double mean(List<Double> doubles) { double sum = 0; for (Double val : doubles) { sum += val; }; return sum/doubles.size(); } }
ArithmeticException.class) public void testMedian_EmptyList(){ StatsCalculator statsCalculator = new StatsCalculator(); statsCalculator.mean(new ArrayList<Double>()); } } public class StatsCalculator { public double mean(List<Double> doubles) { double sum = 0; for (Double val : doubles) { sum += val; }; return sum/doubles.size(); } }
void testMedian_EmptyList(){ StatsCalculator statsCalculator = new StatsCalculator(); try { statsCalculator.mean(new ArrayList<Double>()); fail(“should have thrown an exception. Never should get here”); } catch(ArithmeticException e){} } } public class StatsCalculator { public double mean(List<Double> doubles) { if(doubles.isEmpty()){ throw new ArithmeticException("Can't take mean of an empty list"); } double sum = 0; for (Double val : doubles) { sum += val; }; return sum/doubles.size(); } }
make a change (entrypoint). 2. Write a test @ that layer, mocking collaborators that represent significant architectural boundaries. Get them to pass. 3. Write tests for said collaborators, making them pass one at a time.
out of the service in real life (usually you don’t just care if something succeeded or failed, but also why). • Would never use field injection ;). Just did it to save space. Unit tests abhor field injection.
omitted for // brevity public boolean deleteUser(String userId){ legacyUserClient.removeUser(userId); return true; }} public class UserServiceTest { //… Test setup omitted for brevity @Test public void deleteUser_handlesErrors() throws Exception { when(legacyUserClient.removeUser(anyString()).thenReturn(“ Oops! Not A success!!”); boolean result = userService.deleteUser(“4354ABC”); \ assertThat(result, equalTo(true)); verify(legacyUserClient).removeUser(“4354ABC”); } }
little contrived, but often its a good pattern to have a “dumb” client that doesn’t interpret semantics of a 3rd party system in terms of your domain (just does structural transformations. On top of that you build a domain-specific service. Notes (aka a wall of text to skip during the presentation)