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.

100% Code Coverage - TDD mit Java EE

394 views

Published on

Unit-Tests sind bei der Java-Entwicklung allgegenwärtig. Und das ist gut so. Aber beim Einsatz von Java EE tun sich viele Entwickler schwer damit, ihre Applikation automatisiert zu testen. Die "Magie" der verschiedenen Frameworks macht Tests umständlich oder scheinbar überflüssig. Wer will schon ein komplettes Deployment in einen Application Server durchführen, nur um einen einfachen Unit-Test auszuführen? Und CDI und JPA werden doch wohl korrekt in der Produktionsumgebung laufen, oder?

Dieser Vortrag zeigt, dass automatische Tests von Java-EE-Anwendungen weder umständlich, noch überflüssig sind. Im Gegenteil: Fast alle Komponenten einer üblichen Java-EE-Anwendung - wie Models, Repositorys, Services, Controller oder REST-Ressourcen - können ohne großen Programmieraufwand isoliert voneinander getestet werden. Und auch für den Test des korrekten Zusammenspiels der einzelnen Spezifikationen - wie JPA, JSF, CDI, JAX-RS oder Bean Validation - gibt es Frameworks, die dem Entwickler das Leben vereinfachen.

Von "einfachen" Unit-Tests mit JUnit 5, Spock und Mockito, über leichtgewichtige Integrationstests mit Derby und Jetty, bis hin zu kompletten End-to-End-Tests mit Arquillian, Selenium und WireMock werden alle Möglichkeiten für automatisierte Tests von Java-EE-Komponenten vorgestellt. Danach gibt es keine Ausrede mehr für weniger als 100% Code Coverage!

Published in: Software
  • Be the first to comment

100% Code Coverage - TDD mit Java EE

  1. 1. TDD mit Java EE 100% Code Coverage
  2. 2. Schreibst du Unit-Tests? 85% 97%ZeroTurnaround, 2014 2164 Teilnehmer Hubstaff, 2014 mehrere 100 Teilnehmer Na klar!
  3. 3. Schreibst du Browser-Tests? 33% 30%ZeroTurnaround, 2014 2164 Teilnehmer Hubstaff, 2014 mehrere 100 Teilnehmer Ja!?
  4. 4. ZeroTurnaround, 2014 2164 Teilnehmer 29.0% 41.5% 82.5% Selenium Mockito JUnit Welche Tools setzt du ein?
  5. 5. Schreibst du gerne Tests? 66% 35%Hubstaff, 2014 mehrere 100 Teilnehmer Ja Immer
  6. 6. Stefan Macke
  7. 7. Stefan Macke http://soa.rocks
  8. 8. Stefan Macke http://soa.rocks @StefanMacke
  9. 9. Stefan Macke http://soa.rocks anwendungsentwickler podcast.de @StefanMacke
  10. 10. JSF
  11. 11. JPA
  12. 12. Bean Validation
  13. 13. JAX-RS
  14. 14. REST
  15. 15. Allgemeines
  16. 16. https://github.com/StefanMacke/javaeetests
  17. 17. System Integration Unit
  18. 18. 100%
  19. 19. 62% 71% 72%
  20. 20. Geschäftslogik
  21. 21. http://junit.org/
  22. 22. sut = new UserName("validusername"); „system under test“
  23. 23. http://joel-costigliola.github.io/assertj/ AssertJ assertThat( new Password("AsdfFdsa").getValue()) .startsWith("Asdf") .endsWith("Fdsa") .isEqualTo("AsdfFdsa");
  24. 24. https://github.com/orien/bean-matchers BeanMatchers assertThat(Article.class, allOf( hasValidBeanConstructor(), hasValidGettersAndSetters(), hasValidBeanHashCode(), hasValidBeanEquals(), hasValidBeanToString()));
  25. 25. public class User { @NotNull(message="User name may not be null") private UserName userName; @Size(min=1, message="Password too short") private Password password;
  26. 26. Validator validator = Validation.buildDefaultValidatorFactory() .getValidator(); Set<ConstraintViolation<User>> constraintViolations = validator.validate(sut); assertThat(constraintViolations.size(), is(3)); http://hibernate.org/validator/
  27. 27. sut = new UserService(); userRepository = mock(UserRepository.class); sut.userRepository = userRepository; when(userRepository.findByUserName(USER_NAME)) .thenReturn(Optional.empty()); assertThat(sut.isValidUser(USER_NAME, PASSWORD), is(false)); http://mockito.org/
  28. 28. public class UserService { @Inject UserRepository userRepository;
  29. 29. mockStatic(Salt.class); when(Salt.generate()).thenReturn( new Salt("asdfasdf")); User user = sut.createUser(...); assertThat(user.getSalt().getValue(), is("asdfasdf")); https://github.com/powermock/powermock
  30. 30. public void notifyAdminWhenUserIsCreated() { sut.createUser(USER_NAME, PASSWORD); verify(mailService) .notifyAdmin("New user created."); http://mockito.org/
  31. 31. logger = mock(Logger.class); sut.logger = logger;
  32. 32. Persistenz
  33. 33. %
  34. 34. em = mock(EntityManager.class); query = mock(TypedQuery.class); sut = new JpaUserRepository(em);
  35. 35. private void givenTheUserDoesNotExist() { when(query.getSingleResult()) .thenThrow(NoResultException.class); when(em.createQuery( any(String.class), eq(User.class))) .thenReturn(query);
  36. 36. givenTheUserDoesNotExist(); Optional<User> result = sut.findByUserName("nonexistinguser"); assertThat(result.isPresent(), is(false));
  37. 37. @Entity(name = "Users") public class User { @Id @GeneratedValue private long id; private UserName userName;
  38. 38. Datenbank JPA http://www.eclipse.org/eclipselink/https://db.apache.org/derby/
  39. 39. <persistence-unit name="IntegrationTest"> <properties> <property name="target-database" value="Derby" /> <property name="url" value="jdbc:derby:./Test.db;create=true" /> <property name="ddl-generation" value="drop-and-create-tables" /> src/integrationTest/resources/META-INF/persistence.xml
  40. 40. @BeforeAll public static void setupClass() { EntityManager em = getEntityManager(); EntityTransaction t = em.getTransaction(); t.begin(); new TestDataCreator().createTestData(em); t.commit();
  41. 41. public void createTestData(EntityManager em) { User user = TestData.validUser(); em.persist(user); }
  42. 42. sut = new JpaUserRepository(getEntityManager()); User expectedUser = TestData.validUser(); User actualUser = sut.findByUserName(...); assertThat(actualUser, is(expectedUser)),
  43. 43. Controller
  44. 44. public String logout() { facesContext.addMessage(null, new FacesMessage( FacesMessage.SEVERITY_INFO, "Goodbye!", null)); return INDEX_PAGE;
  45. 45. facesContext = mock(FacesContext.class); sut.facesContext = facesContext; final String targetPage = sut.logout(); assertThat(targetPage, is(LoginController.INDEX_PAGE)); verify(facesContext).addMessage(isNull(), message.capture()); assertThat(message.getValue().getDetail(), is("Goodbye!"));
  46. 46. REST-API
  47. 47. sut = new SessionResource(); sut.userService = mock(UserService.class); givenCredentialsAreInvalid(); assertThat(sut.authenticateUser(credentials), is(Response.Status.UNAUTHORIZED));
  48. 48. @Path(SessionResource.PATH) public class SessionResource { public static String PATH = "/sessions"; @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Response authenticateUser(
  49. 49. @BeforeAll public static void setupClass() { JETTY_SERVER = new Server(JETTY_PORT); JETTY_SERVER.start(); } @AfterAll public static void teardownClass() { JETTY_SERVER.stop(); } http://www.eclipse.org/jetty/
  50. 50. Response response = ClientBuilder.newClient() .target(DemoApplication.getRestUrl()) .path("/sessions") .request(MediaType.APPLICATION_JSON) .post(Entity.json(validCredentials)); assertThat(response.getStatus(), is(200)); assertThat(response.readEntity(Boolean.class), is(true)); https://jersey.github.io/
  51. 51. given() .contentType(ContentType.JSON) .accept(ContentType.JSON) .body("{ "userName": "validusername", "password": "ValidPassword" }") .when() .post(DemoApplication.getRestUrl() + "/sessions") .then() .statusCode(200) .body(is("true")); https://github.com/rest-assured/rest-assured
  52. 52. EE-Container
  53. 53. http://arquillian.org/
  54. 54. arquillian.xml Chameleon repo1.maven.org wildfly:10.0.0:remote
  55. 55. a4d2-f27-91af.war persistence.xml TestDataCreator @Observes java:/ExampleDS create-drop
  56. 56. war H2 IntegrationTest WebTarget .../a4d2-f27-91af/ @Test
  57. 57. Frontend
  58. 58. Webdriver http://www.seleniumhq.org/
  59. 59. Drone und Graphene http://arquillian.org/modules/drone-extension/ http://arquillian.org/modules/graphene-extension/
  60. 60. public class LoginPage { static final By LOGIN_BUTTON = By.id("loginForm:login"); static final By LOGGED_IN = By.xpath("//h1[contains(text(),'Welcome')]"); public void login(String name, String password) { driver.get(contextPath + "index.xhtml"); driver.findElement(USERNAME_FIELD).sendKeys(name); driver.findElement(LOGIN_BUTTON).click();
  61. 61. Externe Services
  62. 62. String json = "[ {" + " "title": "The first title"" + "} ]"; List<Article> articles = sut.createArticles(json); assertThat(articles.get(0).getTitle(), is("The first title"));
  63. 63. public class ArticleService { static String TARGET_SERVER = "http://serviceorientedarchitect.com"; static String TARGET_PATH = "/wp-json/wp/v2/posts";
  64. 64. stubFor(get(urlPathEqualTo("articles")) .withQueryParam("per_page", equalTo("2")) .willReturn( aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("The first title"))); assertThat(first.getTitle(), is("The first title")); http://wiremock.org/
  65. 65. http://www.citrusframework.org/
  66. 66. Fazit
  67. 67. http://soa.rocks anwendungsentwickler podcast.de @StefanMacke TDD mit Java EE 100% Code Coverage StefanMacke/javaeetests

×