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.

JDD 2016 - Sebastian Malaca - You Dont Need Unit Tests

37 views

Published on

How often did you hear developers who were proving quality of their application’s code by sharing information about really high coverage and a huge number of unit tests? How often did you hear how unit tests can make your life easier. How having unit tests makes it possible to introduce changes and make refactoring safier? Do you believe in it? What’s your experience?
I will show you why all of those “proofs” are invalid and wrong. I will show you the false impression of feeling safe in those dangerous places where your code is covered only with tones of unit tests.

But it won’t be all. I won’t leave you alone in this place. I will tell you what you can do to make things better.
I will show you the difference between coverage and quality. Between unit tests and safety.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

JDD 2016 - Sebastian Malaca - You Dont Need Unit Tests

  1. 1. You don’t need unit tests! Sebastian Malaca
  2. 2. Who am I? Fanatic of the OOP and the Code’s Quality Blogger Speaker Trainer and consultant Software Developer at Luxoft @SebastianMalaca letstalkaboutjava.blogspot.com
  3. 3. Let me tell you a story…
  4. 4. Unit tests - what we will gain? Documentation Easy to Refactor Easy to Modify Testable Code = good Design Safety
  5. 5. Not so good….
  6. 6. Documentation? Or implementation? void apply(ClassCode code) { if (code.isComplex() || code.isUnreadable()) { refactor(code); } } @Test public void shouldRefactorCodeWhenIsUnreadable() { ... } @Test public void shouldRefactorCodeWhenIsComplex() { ... } @Test public void shouldNotRefactorCodeWhenIsNotComplexNorUnreadable() { ... }
  7. 7. Documentation? Or implementation? void apply(ClassCode code) { if (code.isComplexOrUnreadable()) { refactor(code); } } @Test public void shouldRefactorCodeWhenIsComplexOrUnreadable() { ... } @Test public void shouldNotRefactorCodeWhenIsNotComplexNorUnreadable() { ... }
  8. 8. Documentation? Or implementation? void apply(ClassCode code) { if (code.isComplexOrUnreadable() && code.isEditable()) { refactor(code); } } @Test public void shouldRefactorCodeWhenIsComplexOrUnreadableAndEditable() { ... } @Test public void shouldNotRefactorCodeWhenIsComplexOrUnreadableAndNotEditable() { ... } @Test public void shouldNotRefactorCodeWhenIsNotComplexNorUnreadableAndEditable () { ... } @Test public void shouldNotRefactorCodeWhenIsNotComplexNorUnreadableAndNotEditable () { ... }
  9. 9. What you have to understand to understand tests? void apply(ClassCode code) { if (code.isComplex() || code.isUnreadable()) { refactor(code); } } complex unreadable yes yes yes no no yes no no
  10. 10. What you have to understand to understand tests? @Test public void shouldRefactorCodeWhenIsComplexAndUnreadable() { ClassCode code = mock(ClassCode.class); given(code.isComplex()).willReturn(true); given(code.isUnreadable()).willReturn(true); processor.apply(code); // assertion } void apply(ClassCode code) { if (code.isComplex() || code.isUnreadable()) { refactor(code); } } complex unreadable yes yes yes no no yes no no
  11. 11. What you have to understand to understand tests? @Test public void shouldRefactorCodeWhenIsComplex() { ClassCode code = mock(ClassCode.class); given(code.isComplex()).willReturn(true); processor.apply(code); // assertion } void apply(ClassCode code) { if (code.isComplex() || code.isUnreadable()) { refactor(code); } } complex unreadable yes yes yes no no yes no no
  12. 12. What you have to understand to understand tests? @Test public void shouldRefactorCodeWhenIsUnreadable() { ClassCode code = mock(ClassCode.class); given(code.isUnreadable()).willReturn(true); processor.apply(code); // assertion } void apply(ClassCode code) { if (code.isComplex() || code.isUnreadable()) { refactor(code); } } complex unreadable yes yes yes no no yes no no
  13. 13. What you have to understand to understand tests? @Test public void shouldNotRefactorCodeWhenIsNotComplexNorUnreadable() { ClassCode code = mock(ClassCode.class); processor.apply(code); // assertion } void apply(ClassCode code) { if (code.isComplex() || code.isUnreadable()) { refactor(code); } } complex unreadable yes yes yes no no yes no no
  14. 14. Too much is too much… void refactor(ClassCode code, Developer developer) { if (gitRepository.testsExistsFor(code.getClassName())) { Roles roles = developer.getRoles(); if (code.mayBeModifiedBy(roles)) { GitLogin login = developer.getGitLogin(); startRefactoringOf(code, login); } } }
  15. 15. void refactor(ClassCode code, Developer developer) { if (gitRepository.testsExistsFor(code.getClassName())) { Roles roles = developer.getRoles(); if (code.mayBeModifiedBy(roles)) { GitLogin login = developer.getGitLogin(); startRefactoringOf(code, login); } } } Too much is too much… @Test public void shouldNotRefactorWhenTestsDoesNotExist() { ... } @Test public void shouldNotRefactorWhenTestsExistsAndDeveloperCannotModifyCode() { ... } @Test public void shouldRefactorWhenTestsExistsAndDeveloperCanModifyCode() { ... }
  16. 16. void refactor(ClassCode code, Developer developer) { if (gitRepository.testsExistsFor(code.getClassName())) { Roles roles = developer.getRoles(); if (code.mayBeModifiedBy(roles)) { GitLogin login = developer.getGitLogin(); startRefactoringOf(code, login); } } } Too much is too much… private static final GitLogin SOME_GIT_LOGIN = mock(GitLogin.class); private static final Roles SOME_ROLES = mock(Roles.class); private static final String SOME_CLASS_NAME = "SomeClass"; @Mock private ClassCode code; @Mock private Developer developer; @Before public void init() { given(code.getClassName()).willReturn(SOME_CLASS_NAME); given(developer.getRoles()).willReturn(SOME_ROLES); given(developer.getGitLogin()).willReturn(SOME_GIT_LOGIN); }
  17. 17. Do you really feel safe? public class ProcessingHandler { private final List<Processor> processors; public ProcessingHandler(List<Processor> processors) { this.processors = processors; } public Output process(Input input) { for (Processor processor : processors) { if (processor.isApplicableFor(input)) { return processor.process(input); } } return DEFAULT_OUTPUT; } }
  18. 18. Do you really feel safe? public Output process(Input input) { for (Processor processor : processors) { if (processor.isApplicableFor(input)) { return processor.process(input); } } return DEFAULT_OUTPUT; } @Test public void shouldReturnDefaultOutputWhenApplicableProcessorNotFound() { ... } @Test public void shouldReturnOutputCreatedByApplicableProcessor() { ... }
  19. 19. Do you really feel safe? public class ProcessingHandler { private final List<Processor> processors; public ProcessingHandler(List<Processor> processors) { this.processors = processors; } // some code } <bean id="processingHandler" class="com.smalaca.processor.ProcessingHandler"> <constructor-arg name="processors"> <bean class="com.smalaca.processor.ConcreteProcessor1"/> <bean class="com.smalaca.processor.ConcreteProcessor2"/> <bean class="com.smalaca.processor.ConcreteProcessor3"/> </constructor-arg> </bean>
  20. 20. Assumptions may change… public class ConcreteProcessor1 extends Processor { public Output process(Input input) { return anOututFor(input); } // some code }
  21. 21. Assumptions may change… public class ConcreteProcessor1 extends Processor { public Output process(Input input) { if (isEverythingOk(input)) { return anOututFor(input); } return null; } // some code } public class ConcreteProcessor1 extends Processor { public Output process(Input input) { return anOututFor(input); } // some code }
  22. 22. Assumptions may change… public Output process(Input input) { for (Processor processor : processors) { if (processor.isApplicableFor(input)) { return processor.process(input); } } return DEFAULT_OUTPUT; } public class ConcreteProcessor1 extends Processor { public Output process(Input input) { if (isEverythingOk(input)) { return anOututFor(input); } return null; } // some code }
  23. 23. Assumptions may change… public Output process(Input input) { for (Processor processor : processors) { if (processor.isApplicableFor(input)) { return processor.process(input); } } return DEFAULT_OUTPUT; } public class ConcreteProcessor1 extends Processor { public Output process(Input input) { if (isEverythingOk(input)) { return anOututFor(input); } return null; } // some code }
  24. 24. Assumptions may change… public Output process(Input input) { if (processor.isApplicableFor(input)) { return processor.process(input); } return DEFAULT_OUTPUT; } public class ConcreteProcessor1 extends Processor { public Output process(Input input) { if (isEverythingOk(input)) { return anOututFor(input); } return null; } // some code }
  25. 25. Assumptions may change… @Test public void shouldReturnDefaultOutputWhenApplicableProcessorNotFound() { given(processor.isApplicableFor(SOME_INPUT)).willReturn(false); // some code } @Test public void shouldReturnOutputCreatedByApplicableProcessor() { given(processor.isApplicableFor(SOME_INPUT)).willReturn(true); given(processor.process(SOME_INPUT)).willReturn(SOME_OUTPUT); // some code } public Output process(Input input) { if (processor.isApplicableFor(input)) { return processor.process(input); } return DEFAULT_OUTPUT; }
  26. 26. You really can feel safe…
  27. 27. Implementation or funcationality? Why, not How Modifiers and accesors?
  28. 28. TDD – be lazy in a good way void apply(ClassCode code) { if (code.isComplex() || code.isUnreadable()) { refactor(code); } }
  29. 29. TDD – be lazy in a good way @Test public void shouldRefactorCodeWhenCodeShouldBeImproved() { given(code.shouldBeImproved()).willReturn(true); // test } @Test public void shouldNotRefactorCodeWhenCodeDoesNotHaveToBeImproved() { given(code.shouldBeImproved()).willReturn(false); // test } void apply(ClassCode code) { if (code.isComplex() || code.isUnreadable()) { refactor(code); } }
  30. 30. Component Tests! Normal flow Most critical scenarios Public API and package private Refactoring/Redesign
  31. 31. Boundaries Boundary Object Integration/Component Tests What to mock and what not to?
  32. 32. Don’t Mock everything! Less assumptions Creation is light Input Change = fast feedback You are the owner
  33. 33. Do you really need unit tests?

×