Developer Test - Things to Know

Download Developer Test - Things to Know

Post on 05-Dec-2014

93 views

Category:

Engineering

3 download

Embed Size (px)

DESCRIPTION

Presenation slides given at Kaunas JUG 2014-10-15

TRANSCRIPT

<ul><li> 1. Developer Tests Things to Know Vaidas Pilkauskas 2014 Kaunas JUG vaidas.pilkauskas@gmail.com </li> <li> 2. We couldnt understand why people without technical knowledge had to tell programmers what to do and, furthermore, they had to supervise how programmers did it. Cristian Rennella http://qz.com/260846/why-our-startup-has-no-bosses-no-office-and-a-four-day-work-week/ </li> <li> 3. What Im going to talk about Things we argue about during code reviews Things that took me time to understand and prove that they are actually good way to go Small things we have no time to discuss during big talks </li> <li> 4. Legacy Code is code without Tests Michael Feathers Working Effectively with Legacy Code </li> <li> 5. So what is test? It is systems exercise under predefined conditions and then verification of an expected outcome. </li> <li> 6. Thing #1 Test phases </li> <li> 7. Test phases 1. Set up 2. Exercise 3. Verify 4. Tear down </li> <li> 8. Test phases @Test public void serverShouldExecuteJobSuccessfully() { Server server = new Server(); // set up Job job = new Job(); // set up Status status = server.execute(job); // exercise assertEquals(SUCCESS, status); // verify server.shutdown(); // tear down } </li> <li> 9. @Before public void before() { server = new Server(); } @Test public void serverShouldExecuteJobSuccessfully() { Job job = new Job(); // set up Status status = server.execute(job); // exercise assertEquals(SUCCESS, status); // verify server.shutdown(); // tear down } </li> <li> 10. @Before public void before() { server = new Server(); Job job = new Job(); } @Test public void serverShouldExecuteJobSuccessfully() { Status status = server.execute(job); // exercise assertEquals(SUCCESS, status); // verify server.shutdown(); // tear down } </li> <li> 11. @Test public void serverShouldQueueJobWithFutureDate() { // * set up which is actual for the current method // * use scope specific name Job futureJob = new Job(futureDate()); // set up Status status = server.execute(futureJob); // exercise assertEquals(SUCCESS, status); // verify server.shutdown(); // tear down } </li> <li> 12. @Before public void before() { server = new Server(); Job job = new Job(); } @Test public void serverShouldExecuteJobSuccessfully() { Status status = server.execute(job); // exercise assertEquals(SUCCESS, status); // verify } @After public void after() { server.shutdown(); // tear down } </li> <li> 13. @Before .. @Test public void serverShouldExecuteJobSuccessfully() { // * no need to name intermediate var, but // * may hide return meaning of server.execute() // execute &amp; verify assertEquals(SUCCESS, server.execute(job)); } @After .. </li> <li> 14. Set up DRY principle </li> <li> 15. Set up DRY principle Readability </li> <li> 16. Set up DRY principle Readability Consistency </li> <li> 17. Set up DRY principle Readability Consistency Complexity </li> <li> 18. Refactoring Refactoring is about improving the design of existing code. It is the process of changing a software system in such a way that it does not alter the external behavior of the code, yet improves its internal structure. Martin Fowler Refactoring: Improving the Design of Existing Code </li> <li> 19. Thing #2 What do we test? </li> <li> 20. Test behaviour not methods Think of a contract </li> <li> 21. Test behaviour not methods Think of a contract And responsibilities </li> <li> 22. Test behaviour not methods Think of a contract And responsibilities Specify requirements as tests </li> <li> 23. Test behaviour not methods Think of a contract And responsibilities Specify requirements as tests Happens naturally when done in test-first approach </li> <li> 24. Thing #3 Matchers </li> <li> 25. Matchers Enhanced readability Assertions on the right level of abstraction Encapsulate testing logic Reusable Detailed match error messages (do not leave them out in your custom matchers!) </li> <li> 26. Matcher libraries Hamcrest - standard matcher lib for JUnit AssertJ - fluent assertions (IDE friendly) Bring common matchers for you to use Write your own custom matchers </li> <li> 27. Hamcrest assertThat(theBiscuit, equalTo(myBiscuit)); assertThat(theBiscuit, is(equalTo(myBiscuit))); assertThat(theBiscuit, is(myBiscuit)); </li> <li> 28. AssertJ assertThat(frodo.getName()).isEqualTo("Frodo"); assertThat(frodo).isNotEqualTo(sauron) .isIn(fellowshipOfTheRing); assertThat(sauron).isNotIn(fellowshipOfTheRing); </li> <li> 29. Thing #4 Custom matchers </li> <li> 30. Custom matchers Are matchers we develop specifically for our projects </li> <li> 31. Custom matchers Help communicate test intention Abstract assertion logic in case standard matchers are not enough Are reusable and save time in large projects You may have custom message to be more specific about test failure </li> <li> 32. Custom matchers @Test public void testBookIsbn() { Book book = new Book(1l, "5555", "A book"); assertThat(book, hasIsbn("1234")); } </li> <li> 33. Thing #5 Failing test </li> <li> 34. fail() In some cases like testing exceptions you may want to force test to fail if some expected situation does not happen </li> <li> 35. fail() try{ // do stuff... fail("Exception not thrown"); }catch(Exception e){ assertTrue(e.hasSomeFlag()); } </li> <li> 36. fail() Fundamentally not bad, but better use matchers for expected failure Matchers help to clarify test intention Dont forget - expected behaviour is an opposite of a failing test </li> <li> 37. Thing #6 Anti-pattern: The Ugly Mirror </li> <li> 38. Anti-pattern: The Ugly Mirror @Test public void personToStringShouldIncludeNameAndSurname() { Person person = new Person("Vilkas", "Pilkas"); String expected = "Person[" + person.getName() + " " + person.getSurname() + "]" assertEquals(expected, person.toString()); } </li> <li> 39. Anti-pattern: The Ugly Mirror @Test public void personToStringShouldIncludeNameAndSurname() { Person person = new Person("Vilkas", "Pilkas"); String expected = "Person[" + person.getName() + " " + person.getSurname() + "]" assertEquals(expected, person.toString()); } </li> <li> 40. Anti-pattern: The Ugly Mirror @Test public void personToStringShouldIncludeNameAndSurname() { Person person = new Person("Vilkas", "Pilkas"); assertEquals("Person[Vilkas Pilkas]", person.toString()); } </li> <li> 41. Thing #7 How to turn off the test? </li> <li> 42. Why would you want to turn off the test? Well, because it fails :) </li> <li> 43. Ignoring tests Always use ignore/pending API from your test library (JUnit @Ignore) </li> <li> 44. Ignoring tests Always use ignore/pending API from your test library (JUnit @Ignore) Do not comment out or false assert your test </li> <li> 45. Ignoring tests Always use ignore/pending API from your test library (JUnit @Ignore) Do not comment out or false assert your test If you do not need a test - delete it </li> <li> 46. Thing #8 What to do with exceptions? </li> <li> 47. Exceptions If you can, use matchers instead of @Test(expected=?) </li> <li> 48. JUnit expected exception @Test(expected=IndexOutOfBoundsException.class) public void shouldThrowIndexOutOfBoundsException() { ArrayList emptyList = new ArrayList(); Object o = emptyList.get(0); } //matcher in Specs2 (Scala) server.process(None) must throwA[NothingToProccess] </li> <li> 49. Exceptions If you can, use matchers instead of @Test(expected=?) try-catch approach </li> <li> 50. try and catch public void shouldThrowIndexOutOfBoundsException() { ArrayList emptyList = new ArrayList(); try { Object o = emptyList.get(0); fail("Should throw IndexOutOfBoundsException"); } catch(IndexOutOfBoundsException e)){ //consider asserting message! } } </li> <li> 51. Exceptions If you can, use matchers instead of @Test(expected=?) try-catch approach catch-exception lib </li> <li> 52. catch-exception lib List myList = new ArrayList(); catchException(myList).get(1); assertThat(caughtException(), allOf( is(IndexOutOfBoundsException.class), ha...</li></ul>