Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

JavaOne 2017 - TestContainers: integration testing without the hassle

815 visualizaciones

Publicado el

Integration testing of Java applications with TestContainers

Publicado en: Tecnología
  • Sé el primero en comentar

  • Sé el primero en recomendar esto

JavaOne 2017 - TestContainers: integration testing without the hassle

  1. 1. TestContainers Integration testing without the hassle @antonarhipov
  2. 2. Why integration testing?
  3. 3. 1. # of tests per time unit
  4. 4. 1. # of tests per time unit 2. Complex setup
  5. 5. 1. Reproducible environment
  6. 6. 1. Reproducible environment 2. Both for development and CI
  7. 7. 1. Reproducible environment 2. Both for development and CI 3. Isolated
  8. 8. 1. Reproducible environment 2. Both for development and CI 3. Isolated 4. As real as possible
  9. 9. 1. Reproducible environment 2. Both for development and CI 3. Isolated 4. As real as possible 5. Cross-platform
  10. 10. 1. Reproducible environment 2. Both for development and CI 3. Isolated 4. As real as possible 5. Cross-platform 6. Easy to set up, use & maintain
  11. 11. Docker Compose FTW! redis:
 image: redis
 ports:
 - "6379:6379"
 postgres:
 image: postgres
 ports:
 - "5432:5432"
 elasticsearch:
 image: elasticsearch:5.0.0
 ports:
 - "9200:9200"

  12. 12. TestContainers to the rescue!
  13. 13. How does it work? https://github.com/docker-java/docker-java Docker environment discovery Will start docker machine if it’s not started yet Containers cleanup on JVM shutdown
  14. 14. GenericContainer redis = new GenericContainer("redis:3.0.6") .withExposedPorts(6379);
  15. 15. GenericContainer redis = new GenericContainer("redis:3.0.6") .withExposedPorts(6379); PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.2") .withUsername(POSTGRES_USERNAME) .withPassword(POSTGRES_PASSWORD);
  16. 16. TestContainers https://github.com/testcontainers/testcontainers-java
  17. 17. Docker Compose? - Yes! public static DockerComposeContainer env = new DockerComposeContainer( new File("src/test/resources/compose-test.yml")) .withExposedService("redis_1", REDIS_PORT);
  18. 18. Docker Compose? - Yes! public static DockerComposeContainer env = new DockerComposeContainer( new File("src/test/resources/compose-test.yml")) .withExposedService("redis_1", REDIS_PORT); String url = env.getServiceHost("redis_1", REDIS_PORT); Integer port = env.getServicePort("redis_1", REDIS_PORT); Jedis jedis = new Jedis(url, port);
  19. 19. Microservices!11 REST service Java, Spring Boot Redis & PostgreSQL Calls some other micro-services
  20. 20. @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT) @ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class) public abstract class AbstractIntegrationTest { @ClassRule public static PostgreSQLContainer postgreSql = new PostgreSQLContainer(); @ClassRule public static GenericContainer redis = new GenericContainer("redis:3.0.6") .withExposedPorts(6379); @ClassRule public static MockServerContainer mockServer = new MockServerContainer();
  21. 21. @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT) @ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class) public abstract class AbstractIntegrationTest { @ClassRule public static PostgreSQLContainer postgreSql = new PostgreSQLContainer(); @ClassRule public static GenericContainer redis = new GenericContainer("redis:3.0.6") .withExposedPorts(6379); @ClassRule public static MockServerContainer mockServer = new MockServerContainer(); Still using all the Spring goodies!
  22. 22. @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT) @ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class) public abstract class AbstractIntegrationTest { @ClassRule public static PostgreSQLContainer postgreSql = new PostgreSQLContainer(); @ClassRule public static GenericContainer redis = new GenericContainer("redis:3.0.6") .withExposedPorts(6379); @ClassRule public static MockServerContainer mockServer = new MockServerContainer(); External dependencies
  23. 23. public class MockServerContainer<SELF extends MockServerContainer<SELF>> extends GenericContainer<SELF> { public MockServerContainer() { super("jamesdbloom/mockserver:latest"); addExposedPorts(8080); } private MockServerClient client; @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { super.containerIsStarted(containerInfo); client = new MockServerClient(getContainerIpAddress(), getMappedPort(getExposedPorts().get(0))); } }
  24. 24. public class MockServerContainer<SELF extends MockServerContainer<SELF>> extends GenericContainer<SELF> { public MockServerContainer() { super("jamesdbloom/mockserver:latest"); addExposedPorts(8080); } private MockServerClient client; @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { super.containerIsStarted(containerInfo); client = new MockServerClient(getContainerIpAddress(), getMappedPort(getExposedPorts().get(0))); } }
  25. 25. MockServerClient client = … client.when( HttpRequest.request("/analytics/issues/" + project.projectId + "/app").withMethod("GET"), Times.unlimited()) .respond(singleIssueResponse());
  26. 26. How to test Java agents?
  27. 27. public static void premain(String args, Instrumentation instrumentation) { instrumentation.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if ("spark/webserver/JettyHandler".equals(className)) { try { ClassPool cp = new ClassPool(); cp.appendClassPath(new LoaderClassPath(loader)); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); CtMethod ctMethod = ct.getDeclaredMethod("doHandle"); ctMethod.insertBefore("{ $4.setHeader("X-My-Super-Header", "header value"); }"); return ct.toBytecode(); } catch (Throwable e) { e.printStackTrace(); } } return classfileBuffer; } }); }
  28. 28. public static void premain(String args, Instrumentation instrumentation) { instrumentation.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if ("spark/webserver/JettyHandler".equals(className)) { try { ClassPool cp = new ClassPool(); cp.appendClassPath(new LoaderClassPath(loader)); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); CtMethod ctMethod = ct.getDeclaredMethod("doHandle"); ctMethod.insertBefore("{ $4.setHeader("X-My-Super-Header", "header value"); }"); return ct.toBytecode(); } catch (Throwable e) { e.printStackTrace(); } } return classfileBuffer; } }); }
  29. 29. public static void premain(String args, Instrumentation instrumentation) { instrumentation.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if ("spark/webserver/JettyHandler".equals(className)) { try { ClassPool cp = new ClassPool(); cp.appendClassPath(new LoaderClassPath(loader)); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); CtMethod ctMethod = ct.getDeclaredMethod("doHandle"); ctMethod.insertBefore("{ $4.setHeader("X-My-Super-Header", "header value"); }"); return ct.toBytecode(); } catch (Throwable e) { e.printStackTrace(); } } return classfileBuffer; } }); }
  30. 30. public class AgentTest { @ClassRule public static BasicTestApp app = new BasicTestApp(); @Test public void testIt() throws Exception { Response response = app.getClient().getHello(); System.out.println("Got response:n" + response); assertThat(response.headers().get("X-My-Super-Header")) .isNotNull() .hasSize(1) .containsExactly("header value"); } }
  31. 31. public class AgentTest { @ClassRule public static BasicTestApp app = new BasicTestApp(); @Test public void testIt() throws Exception { Response response = app.getClient().getHello(); System.out.println("Got response:n" + response); assertThat(response.headers().get("X-My-Super-Header")) .isNotNull() .hasSize(1) .containsExactly("header value"); } } A custom container
  32. 32. public class AgentTest { @ClassRule public static BasicTestApp app = new BasicTestApp(); @Test public void testIt() throws Exception { Response response = app.getClient().getHello(); System.out.println("Got response:n" + response); assertThat(response.headers().get("X-My-Super-Header")) .isNotNull() .hasSize(1) .containsExactly("header value"); } } Verify, if the new behaviour is there
  33. 33. https://github.com/bsideup/javaagent-boilerplate
  34. 34. https://www.testcontainers.org https://twitter.com/testcontainers https://github.com/testcontainers https://testcontainers.slack.com
  35. 35. anton@zeroturnaround.com @antonarhipov slideshare.net/arhan

×