Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

JDD2015: Where Test Doubles can lead you... - Sebastian Malaca

159 views

Published on

WHERE TEST DOUBLES CAN LEAD YOU...

"With great power comes great responsibility". And Test Double Patterns gives you a great power. Your life is easier, tests lighter and faster.
But someday this power can turn against you.
Each day we are more comfortable with using Test Double Patters. We are creating assumptions. We are mocking services. We are ending with well covered code and tests... that tell us nothing.
That's why it's so important to recognize warning signs. To know the pitfalls that are waiting for us. To know what to do in these situations.

Published in: Software
  • Be the first to comment

  • Be the first to like this

JDD2015: Where Test Doubles can lead you... - Sebastian Malaca

  1. 1. Where Test Double can lead you… Sebastian Malaca
  2. 2. What Test Double are? • Control • Power • Behavior • Assumptions
  3. 3. Test Double and its Types • Dummy • Fake • Stub • Spy • Mock
  4. 4. TD Types - Dummy Object public class Blog public void addArticle(Article article) ; public int numberOfArticles() ; @Test public void shouldReturnNumberOfArticlesOnBlog() { blog.addArticle(getDummyArticle()); blog.addArticle(getDummyArticle()); assertThat(blog.numberOfArticles(), is(2)); } private Article getDummyArticle() { return new Article(); }
  5. 5. TD Types – Fake Object public class EventProcessor private Store store; public void add(Event event) { store.add(event); } public void processAll() { for (Event event : store.getAll()) { event.process(); } }
  6. 6. TD Types – Fake Object c.d. public class FakeStore implements Store { private final List<Event> events = new ArrayList<>(); @Override public void add(Event event) { events.add(event); } @Override public List<Event> getAll() { return events; } } @Test public void shouldProcessAll() { Event event1 = spiedEvent(); Event event2 = spiedEvent(); store.add(event1); store.add(event2); processor.processAll(); verify(event1).process(); verify(event2).process(); }
  7. 7. TD Types – Stub Object @Test public void shouldProcessAll() { Event event1 = spiedEvent(); Event event2 = spiedEvent(); stub(store.getAll()).toReturn(asList(event1, event2)); processor.processAll(); verify(event1).process(); verify(event2).process(); }
  8. 8. TD Types – Spy Object public class PublishedArticleEventTest { @Spy private Article article; @Test public void shouldPublishArticle() { PublishedArticleEvent event = new PublishedArticleEvent(article); event.process(); verify(article).published(); } } public class PublishedArticleEvent implements Event { private final Article article; public PublishedArticleEvent(Article article) { this.article = article; } public void process() { article.published(); } }
  9. 9. TD Types – Mock Object @Test public void shouldProcessOnlyThoseWhichSatisfiesPredicate() { Event event1 = spiedEvent(); Event event2 = spiedEvent(); given(store.getAll()).willReturn(asList(event1, event2)); given(predicate.isSatisfiedBy(event1)).willReturn(true); given(predicate.isSatisfiedBy(event2)).willReturn(false); processor.processAll(predicate); then(event1).should().process(); then(event2).should(never()).process(); } // public class EventProcessor public void processAll(EventPredicate predicate) { for (Event event : store.getAll()) { if (predicate.isSatisfiedBy(event)) event.process(); } }
  10. 10. Pitfalls • All we need are Unit Tests • Unreal situation • Aggregation over Composition • Dependency -> Test Double • More Mocks = More fragile Code • Mocks return mocks return …
  11. 11. Make your life easier • Say no to Accessors • No additional behavior in Mocks • No logic in constructors • Object is lighter than mock
  12. 12. Use Parameter Object public void foo(Bar bar, Baz baz, Xyz xyz) { doSomething1(bar.getX(), baz.getZ()); doSomething2(bar.getX(), xyz.getY()); doSomething3(baz.getZ()); } // IN TESTS Z z = new Z(); Y y = new Y(); X x = new X(); Bar bar = new Bar(x); Baz baz = new Baz(z); Xyz xyz = new Xyz(y); public void foo(FooContext context) { doSomething1(context.getX(), context.getZ()); doSomething2(context.getX(), context.getY()); doSomething3(context.getZ()); } // IN TESTS Z z = new Z(); Y y = new Y(); X x = new X(); FooContext context = new FooContext(z, x, y);
  13. 13. Law of Demeter // public class IsAdultPredicate { public boolean apply(Person person) { return person.getAge().getYears() >= ENOUGH; } @Test public void shouldApplyWhenEnough() { int enoughYears = 18; Age age = mock(Age.class); given(age.getYears()).willReturn(enoughYears); Person person = mock(Person.class); given(person.getAge()).willReturn(age); } // public class IsAdultPredicate { public boolean apply(Person person) { return person.getYears() >= ENOUGH; } @Test public void shouldApplyWhenEnough() { int enoughYears = 18; Person person = mock(Person.class); given(person.getYears()).willReturn(enoughYears); }
  14. 14. Tell, don’t ask // public class IsAdultPredicate { public boolean apply(Person person) { return person.isAdult(); } @Test public void shouldApplyWhenEnough() { Person person = mock(Person.class); given(person.isAdult()).willReturn(true); }
  15. 15. Testing implementation • "What" not "how" or "how" not "what"? • Refactoring/Change is not safe • Refactoring/Change affects unit tests, not integration • Implementation leaking
  16. 16. Code extraction • Dependent on Class • Dependent on Class from different module • Dependent on Interface
  17. 17. Boundary Object • Wrapper is your friend • You are the owner • Integration, not Unit! • And what about JDK?
  18. 18. Boundary Object c.d. public class PublishProcessor { private final ExternalReportingService reportingService; private final Notifier notifier; public void process(Report report) { if (isReadyToPublish(report)) { notifier.notifyAll(report); reportingService.publish(report); } } private boolean isReadyToPublish(Report report) { return report.isComplete(); } }
  19. 19. Boundary Object c.d. @Test public void shouldProcessCompletedReport() { ExternalReportingService reportingService = mock(ExternalReportingService.class); Notifier notifier = mock(Notifier.class); PublishProcessor processor = new PublishProcessor(reportingService, notifier); Report report = new Report(); report.completed(); processor.process(report); then(reportingService).should().publish(report); then(notifier).should().notifyAll(report); }
  20. 20. Boundary Object c.d. public class ExternalReportingService { public void publish(Report report, Priority priority); public class ReportingService { private ExternalReportingService reportingService; public ReportingService(ExternalReportingService reportingService) { this.reportingService = reportingService; } public void publish(Report report) { reportingService.publish(report, REGULAR); } }
  21. 21. Too many Test Double? • How many is too many? • Readability • Warning sign • Unit != Unit • Violation of Law of Demeter • Violation of Single Responsibility Principle
  22. 22. Too many Test Double? Improve your tests!

×