Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Testcontainers Cassandra with Spring Data for Apache Cassandra?

I develop integration tests for my spring boot application, which works with Cassandra. I use CassandraTemplate for communication with Cassandra.

I have the following config for the database src/test/resources/application.properties:

spring.data.cassandra.contact-points=cassandra-host
spring.data.cassandra.port=9042

To create Testcontainers with Cassandra I try 2 ways:

1) https://niels.nu/blog/2017/spring-cassandra-integration-tests.html

@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = {ExampleApplication.class})
@ContextConfiguration(initializers = CounterIntegrationTestContainers.Initializer.class)
@EnableConfigurationProperties
public class CounterIntegrationTestContainers extends CounterIntegrationTest {
    @ClassRule
    public static GenericContainer cassandra =
            new GenericContainer("cassandra:3")
                    .withExposedPorts(9042);

    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            EnvironmentTestUtils.addEnvironment(
                "testcontainers",
                configurableApplicationContext.getEnvironment(),
                "cassandra.host=" + cassandra.getContainerIpAddress(),
                "cassandra.port=" + cassandra.getMappedPort(9042)
            );
        }
    }
}

2) https://www.baeldung.com/spring-boot-testcontainers-integration-test - in this example we can see postgresql container, but I change it to Cassandra:

public class BaeldungPostgresqlContainer extends PostgreSQLContainer<BaeldungPostgresqlContainer> {
    private static final String IMAGE_VERSION = "postgres:11.1";
    private static BaeldungPostgresqlContainer container;
 
    private BaeldungPostgresqlContainer() {
        super(IMAGE_VERSION);
    }
 
    public static BaeldungPostgresqlContainer getInstance() {
        if (container == null) {
            container = new BaeldungPostgresqlContainer();
        }
        return container;
    }
 
    @Override
    public void start() {
        super.start();
        System.setProperty("DB_URL", container.getJdbcUrl());
        System.setProperty("DB_USERNAME", container.getUsername());
        System.setProperty("DB_PASSWORD", container.getPassword());
    }
 
    @Override
    public void stop() {
        //do nothing, JVM handles shut down
    }
}

So, I have a problem. When I start tests, I got cannot create CassandraTemplate bean, because Cassandra server is not available. it seems, that Spring Data tries to health check Cassandra before creating of test bean of the database server.

Is there any way to use Testcontainers Cassandra with Spring Data for Apache Cassandra?

like image 443
Max Avatar asked Oct 15 '22 12:10

Max


2 Answers

It seems, that I can run tests with such class:

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(
        initializers = CassandraTestPrjApplicationTests.Initializer.class
)
public class CassandraTestPrjApplicationTests {

    @Test
    public void contextLoads() {
    }

    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            GenericContainer<?> cassandra =
                    new GenericContainer<>("cassandra:3").withExposedPorts(9042);

            cassandra.start();

            TestPropertyValues.of(
                    "spring.data.cassandra.contact-points=" + cassandra.getContainerIpAddress(),
                    "spring.data.cassandra.port=" + cassandra.getMappedPort(9042)
            ).applyTo(applicationContext);
        }
    }
}

and here is my gradle.build:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-cassandra'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    testCompile "org.testcontainers:testcontainers:1.12.0"
    testCompile "org.testcontainers:cassandra:1.12.0"
}
like image 111
Max Avatar answered Dec 05 '22 12:12

Max


Considering the first approach, I'd start with checking the container that is running during the test execution: docker ps and see if it starts (i.e. docker daemon is up) and exposes the correct port.
Then try to telnet to the corresponding host/port to make sure that service starts during the default 60 seconds timeout period.
It seems enough for the container to start, but if telnet does not connect during this period, try to increase it like

 new GenericContainer("cassandra:3")
        .withExposedPorts(9042)
        .withStartupTimeout(Duration.ofMinutes(2));

If the container exposes the correct port, but the test does not connect to it, make sure you use the correct configuration parameters in your test. I see the usage of spring.data.cassandra.contact-points, the example you've sent uses a bit different configuration, check if you the correct configuration parameter in your cluster initialization code (e.g. "${contact-points}" instead of "${container.host}" or vice versa).

One more option to check your integration test logic works as expected (and there's something wrong with testcontainers configuration) is to start Cassandra container and hardcode it's host and port before running the test.

Hope this helps.

like image 20
GoodDok Avatar answered Dec 05 '22 10:12

GoodDok