Presentation is loading. Please wait.

Presentation is loading. Please wait.

Multi-purpose tests (Cool tricks with JUnit) JavaZone 2012 Johannes Brodwall, Principal Architect Steria

Similar presentations


Presentation on theme: "Multi-purpose tests (Cool tricks with JUnit) JavaZone 2012 Johannes Brodwall, Principal Architect Steria"— Presentation transcript:

1 Multi-purpose tests (Cool tricks with JUnit) JavaZone 2012 Johannes Brodwall, Principal Architect Steria Norway @jhannes

2 Part I

3 Properties of good test suites

4 Fast feedback Reasonable confidence in seconds

5 While you type! Holy crap! Get Infinitest! http://infinitest.github.com/

6 Good coverage Never mind measured code coverage Do you catch many potential mistakes?

7 Robust Does refactoring break you tests? Do tests interfere with each other?

8 @Test public void should_filter_people() { Person person = Person.withName("Darth Vader"); Person nonMatching = Person.withName("Anakin Skywalker"); personRepository.savePerson(person); personRepository.savePerson(nonMatching); assertThat(personRepository.findPeople("vade")).contains(person).excludes(nonMatching); } While you type! Holy crap! Get Infinitest! http://infinitest.github.com/ “Act” and “assert” at same level of abstraction Allows for garbage in repository

9 Good test suites Fast feedback – ok confidence in seconds Coverage – breaks when they should Robust – don’t break when they shouldn’t

10 “Integration test” Sacrifice feedback for coverage

11 Part II

12 The configurable test

13 Starting point

14 public class PersonServletTest { private PersonServlet servlet = new PersonServlet(); private HttpServletRequest req = mock(HttpServletReq.class); private HttpServletResponse resp = mock(HttpServletResp.class); private PersonRepository personRepository = mock(PersonRep.class); @Test public void should_save_person() throws IOException { when(req.getParameter("full_name")).thenReturn("Darth Vader"); servlet.doPost(req, resp); verify(personRepository).savePerson(Person.withName("Darth Vader")); }

15 Fake instead of mock

16 public class PersonServletTest { private PersonServlet servlet = new PersonServlet(); private HttpServletRequest req = mock(HttpServletReq.class); private HttpServletResponse resp = mock(HttpServletResp.class); private PersonRepository personRepository = new FakePersonRepository(); @Test public void should_save_person() throws IOException { when(req.getParameter("full_name")).thenReturn("Darth Vader"); servlet.doPost(req, resp); assertThat(personRepository.findAll()).contains(Person.withName("Darth Vader")); }

17 But we want also to use the real thing

18 public class PersonServlet WithRealRepo Test { private PersonServlet servlet = new PersonServlet(); private HttpServletRequest req = mock(HttpServletReq.class); private HttpServletResponse resp = mock(HttpServletResp.class); private PersonRepository personRepository = new JdbcPersonRepository(…); @Test public void should_save_person() throws IOException { when(req.getParameter("full_name")).thenReturn("Darth Vader"); servlet.doPost(req, resp); assertThat(personRepository.findAll()).contains(Person.withName("Darth Vader")); }

19 What if one test could do both?

20 @RunWith(RepositoryTestRunner.class) public class PersonServletTest { private PersonServlet servlet = new PersonServlet(); private HttpServletRequest req = mock(HttpServletReq.class); private HttpServletResponse resp = mock(HttpServletResp.class); private PersonRepository personRepository; public PersonServletTest(PersonRepository personRepo) { this.personRepository = personRepo; } @Test public void should_save_person() throws IOException { … }

21 I learned to write my own test runner by looking at org.junit.runner.Parameterized

22 The runner

23 public class RepositoryTestRunner extends Suite { private static JdbcConnectionPool dataSource = …; public RepositoryTestRunner(Class testClass) { super(testClass, createRunners(testClass)); } private static List createRunners(Class testClass) { List runners = new ArrayList<>(); runners.add(new RepoTestRunner(new FakePersonRepository(), testClass)); runners.add(new RepoTestRunner(new JdbcPersonRepository(dataSource), testClass)); return runners; } public static class RepoTestRunner extends BlockJUnit4ClassRunner {

24 public class RepositoryTestRunner extends Suite { public static class RepoTestRunner extends BlockJUnit4ClassRunner { private PersonRepository personRepo; RepoTestRunner(PersonRepository personRepo, Class testClass) { super(testClass); this.personRepo = personRepo; } protected Object createTest() throws Exception { return getTestClass().getOnlyConstructor().newInstance(personRepo) ; } protected void validateConstructor(List errors) { validateOnlyOneConstructor(errors); }

25 The result

26

27 Keeping it honest

28 @RunWith(RepositoryTestRunner.class) public class PersonRepositoryTest { private PersonRepository personRepository; public PersonRepositoryTest(PersonRepository personRepository) { this.personRepository = personRepository; } @Test public void should_filter_people() { Person person = Person.withName("Darth Vader"); Person nonMatching = Person.withName("Anakin Skywalker"); personRepository.savePerson(person); personRepository.savePerson(nonMatching); assertThat(personRepository.findPeople("vade")).contains(person).excludes(nonMatching); }

29

30 https://github.com/jhannes/javaee-spike

31 Part III

32 The big picture

33 Worth it?

34 Thought experiment: What if you could test your whole application without connecting to anything?

35 Maintain fakes or Speed up realz?

36 Maintain fakes or Speed up realz?

37 Other uses

38 @RunWith(FileTestRunner.class) @FileTestRunner.Directory("src/test/xml/samples") public class XmlSerializationTest { private final File xmlFile; public XmlSerializationTest(File xmlFile) { this.xmlFile = xmlFile; } @Test public void serializedShouldMatch() { assertThat(normalize(Xml.read(xmlFile).toXML())).isEqualTo(normalize(slurp(xmlFile))); }

39

40 https://github.com/jhannes/eaxy

41 Conclusions

42 There’s more to tests than “integration test” or “unit test”

43 Get fast feedback Detect mistakes Only fail when you should

44 Thank you johannes@brodwall.com http://johannesbrodwall.com http://github.com/jhannes http://twitter.com/jhannes

45 “Integration tests or unit tests”

46 Integration tests or unit test?

47 public class JdbcPersonRepositoryTest { private static DataSource dataSource; @BeforeClass public static void createDataSource() { dataSource = JdbcConnectionPool.create(…); JdbcPersonRepository.createDatabaseSchema(dataSource); } PersonRepository personRepo = new JdbcPersonRepository(dataSource); @Test public void should_save_people() { Person person = Person.withName("Johannes Brodwall"); personRepo.savePerson(person); assertThat(personRepo.findPeople(null)).contains(person); }

48 “Integration tests” Connects to services, database, files? Or just integrates whole system? Or integrates system with all services?

49 “Integration tests” are Slow Dependent on external systems Realistic?

50 Integration tests or unit test?

51 public class PersonTest { @Test public void equality() { assertThat(Person.withName("Darth Vader")).isEqualTo(Person.withName("Darth Vader")).isNotEqualTo(Person.withName("Anakin Skywalker")).isNotEqualTo(new Object()).isNotEqualTo(null); } Since you’re asking: FEST-assert http://code.google.com/p/fest/

52 “Unit tests” No resources outside JVM? Only one class? Only one method?

53 “Unit tests” are Very fast Coupled to implementation?

54 Integration tests or unit test?

55 Why does it matter?

56 Who is this talk for? You have written more than a few tests in JUnit You want to find out when you write unit test and when to write integration tests and how to use mocks well You will be able to write your own test-runner for fun and profit

57 What will I cover Testing considerations Unit test or integration test What are good tests Mocks versus fakes Creating your own test runner The mocked test The faked test The multi purpose test What’s next? Eventually abandoned But still using @RunWith in many cases – eg. Files


Download ppt "Multi-purpose tests (Cool tricks with JUnit) JavaZone 2012 Johannes Brodwall, Principal Architect Steria"

Similar presentations


Ads by Google