Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Better Testing with Testcontainers

Better Testing with Testcontainers

Early in the process of writing applications, you have to decide how to run your integration tests. Will they hit a dedicated testing instance of Neo4j that is always running, or will you use the embedded instance?

There is a third option: starting a database before testing and stopping it after testing. Most of the time, this may require some manual scripting around test execution, but there is a much easier method: using testcontainers with a Neo4j module.

In this session, you'll learn how to start new instances of the database along with your tests whenever needed, take control of the lifetime of the container, and expose connection information needed for testing. With this, your testing infrastructure becomes much more portable and there is no need to share credentials of the test server in the future.

Gerrit Meier

November 17, 2022
Tweet

More Decks by Gerrit Meier

Other Decks in Programming

Transcript

  1. 2 Gerrit Meier • Software Engineer at Neo4j • Team

    Spring Data Neo4j and Neo4j OGM • <something with Java>
  2. 12 Pros - Clean instance - Portable test suite -

    No side-effects from the outside Embedded
  3. 13 Cons Pros - Dependency version - JVM version -

    Non-existing network - Memory allocation - Post-mortem debugging - Clean instance - Portable test suite - No side-effects from the outside Embedded
  4. 15 Pros - Separation of application and database - Network

    - No version conflicts - Post-mortem debugging Local instance
  5. 16 Cons Pros - Custom configuration - Setup needed for

    colleagues and CI - Lifecycle control on CI - Separation of application and database - Network - No version conflicts - Post-mortem debugging Local instance
  6. 18 Pros - One time setup - Always on -

    No lifecycle management needed Remote instance a.k.a. the “Test instance”
  7. 19 Cons Pros - Shared database state - Conflicting tests

    - Unexpected test failures - Post-mortem debugging - One time setup - Always on - No lifecycle management needed Remote instance a.k.a. the “Test instance”
  8. 22 Cons Pros - Java API “command line”-style - Ready(?)

    - Orchestration within test setup - Isolated testing Docker
  9. Testcontainers - Fluent wrapper around Docker API - Support for

    a lot of systems out-of-the-box - Aware of readiness(!) - Seamless integration with JUnit (and Spock) - Networks of multiple containers - Copy data for testing
  10. 25 MaterialIT.java @SpringBootTest class MaterialIT { private final MaterialRepository repository;

    private final Neo4jClient neo4jClient; @Autowired MaterialIT(MaterialRepository repository, Neo4jClient neo4jClient) { this.repository = repository; this.neo4jClient = neo4jClient; } }
  11. 27 MaterialIT.java @SpringBootTest class MaterialIT { static GenericContainer<?> container =

    new GenericContainer<>("neo4j:5") .withExposedPorts(7687) .withEnv("NEO4J_AUTH", "neo4j/verysecret"); }
  12. 28 MaterialIT.java @SpringBootTest class MaterialIT { static GenericContainer<?> container =

    new GenericContainer<>("neo4j:5") .withExposedPorts(7687) .withEnv("NEO4J_AUTH", "neo4j/verysecret"); @BeforeAll static void startDatabase() { container.start(); } }
  13. 29 MaterialIT #testingProperties @DynamicPropertySource static void testingProperties( DynamicPropertyRegistry registry) {

    registry.add("spring.neo4j.uri", () -> "neo4j://" + container.getHost() + ":" + container.getMappedPort(7687)); registry.add("spring.neo4j.authentication.username", () -> "neo4j"); registry.add("spring.neo4j.authentication.password", () -> "verysecret"); }
  14. 30 MaterialIT.java @SpringBootTest @Testcontainers class MaterialIT { @Container static GenericContainer<?>

    container = new GenericContainer<>("neo4j:5") .withExposedPorts(7687) .withEnv("NEO4J_AUTH", "neo4j/verysecret"); }
  15. Testcontainers Neo4j - Neo4j readiness aware - Simplified env parameters

    for configuration - Exposes Bolt and HTTP port as default - Neo4j specific API - Admin user setup - Connection related information - Neo4j Labs Plugin activation
  16. 33 MaterialIT.java @SpringBootTest @Testcontainers class MaterialIT { @Container static Neo4jContainer<?>

    container = new Neo4jContainer<>("neo4j:5") .withAdminPassword("verysecret") }
  17. 34 MaterialIT #testingProperties @DynamicPropertySource static void testingProperties( DynamicPropertyRegistry registry) {

    registry.add("spring.neo4j.uri", container::getBoltUrl); registry.add("spring.neo4j.authentication.username", () -> "neo4j"); registry.add("spring.neo4j.authentication.password", container::getAdminPassword); }
  18. 35 MaterialIT #pluginCall void pluginCall() { String apocVersion = neo4jClient

    .query("RETURN apoc.version()") .fetchAs(String.class) .one().get(); Assertions.assertThat(apocVersion) .isEqualTo("5.1.0"); }
  19. 36 MaterialIT #pluginCall void pluginCall() { String apocVersion = neo4jClient

    .query("RETURN apoc.version()") .fetchAs(String.class) .one().get(); Assertions.assertThat(apocVersion) .isEqualTo("5.1.0"); } Unknown function 'apoc.version'
  20. 37 MaterialIT.java @SpringBootTest @Testcontainers class MaterialIT { @Container static Neo4jContainer<?>

    container = new Neo4jContainer<>("neo4j:5") .withAdminPassword("verysecret") .withLabsPlugins(Neo4jLabsPlugin.APOC) }
  21. 38 MaterialIT #pluginCall void pluginCall() { String apocVersion = neo4jClient

    .query("RETURN apoc.version()") .fetchAs(String.class) .one().get(); Assertions.assertThat(apocVersion) .isEqualTo("5.1.0"); }
  22. 39 MaterialIT #pluginCall void pluginCall() { String apocVersion = neo4jClient

    .query("RETURN apoc.version()") .fetchAs(String.class) .one().get(); Assertions.assertThat(apocVersion) .isEqualTo("5.1.0"); }
  23. Conclusion • Reproducible and portable tests • No manual lifecycle

    management (JUnit extension) • For more performance: reusable containers • Covers more than just Neo4j • Create Test Matrix with ease
  24. 44 Create data Neo4j-Migrations Neo4j Migrations: The Lean Way of

    Applying Database Refactorings to Neo4j Michael Simons 15:40 (UTC) 16:40 (CET)