0

I am using Spring Boot and Spring Data Cassandra in my webflux application. I have issue when I do integration test with docker by using testcontainer, Spring doesn't inject the implementation of my cassandra repository in my tests but it always injects Mockito mock object.

For testing I have 2 kind of tests are unitTest and integrationTest with properly profile test and integration.

enter image description here

Below are all my required classes and test classes

My cassandra repository using Spring Data Cassandra

@Repository
public interface UserGroupRepository extends CassandraRepository<UserGroup, String> {

}

For test profile

@ActiveProfiles("test")
@Configuration
public class TestConfig {
  @MockBean
  private CqlSession cassandraSession;

  @MockBean
  private CassandraConverter cassandraConverter;

  @MockBean
  public UserGroupRepository consumerSegmentRepository;

  @MockBean
  private CassandraMappingContext cassandraMapping;

  @MockBean
  private SessionFactoryFactoryBean cassandraSessionFactory;

  @MockBean
  private CassandraAdminTemplate cassandraAdminTemplate;

  @MockBean
  private SessionFactory sessionFactory;
}

For integration profile

My base integration test class which use docker for real cassandra database

public abstract class AbstractTest {

  public static final String CASSANDRA_KEYSPACE = "user_group";
  public static final String CASSANDRA_SCHEMA_SCRIPT = "cassandra/schema.cql";
  public static final String CASSANDRA_IMAGE = "cassandra:3.11.2";
  public static final int CASSANDRA_PORT = 9042;

  private static final Logger log = LoggerFactory.getLogger(AbstractTest.class);

  protected static final CassandraContainer CASSANDRA = new CassandraContainer(CASSANDRA_IMAGE);

  static {
    startCassandra();
  }

  protected static void startCassandra() {
    CASSANDRA.withInitScript(CASSANDRA_SCHEMA_SCRIPT)
            .withExposedPorts(CASSANDRA_PORT)
            .withStartupTimeout(Duration.ofMinutes(5))
            .withReuse(true)
            .start();

    System.setProperty("spring.data.cassandra.keyspace-name", CASSANDRA_KEYSPACE);
    System.setProperty("spring.data.cassandra.contact-points", "localhost:" + CASSANDRA.getMappedPort(9042));
    System.setProperty("spring.data.cassandra.local-datacenter", "datacenter1");
    System.setProperty("spring.data.cassandra.schema-action", "create_if_not_exists");
  }

  public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {

      // Cassandra
      TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
          applicationContext,
          "spring.data.cassandra.keyspace-name=" + CASSANDRA_KEYSPACE,
          "spring.data.cassandra.contact-points=" + "localhost:" + CASSANDRA.getMappedPort(CASSANDRA_PORT),
          "spring.data.cassandra.local-datacenter=" + "datacenter1",
          "spring.data.cassandra.schema-action=" + "create_if_not_exists"
      );

      Runtime.getRuntime().addShutdownHook(new Thread(CASSANDRA::stop));
    }
  }

  
  protected static Session getCassandraSession() {
    return CASSANDRA.getCluster().connect(CASSANDRA_KEYSPACE);
  }

}

My integration test

@ActiveProfiles("integration")
@AutoConfigureWebTestClient
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureDataCassandra
@ContextConfiguration(initializers = AbstractTest.Initializer.class)
@EnableCassandraRepositories(basePackageClasses = {UserGroupRepository.class})
public class UserGroupControllerTest extends AbstractTest {

  private static final Logger log = LoggerFactory.getLogger(UserGroupControllerTest.class);

  @Autowired
  private WebTestClient webTestClient;

  @Autowired
  private UserGroupRepository userGroupRepository;

  @Test
  void test_cassandra_upAndRunning() {
    assertThat(CASSANDRA.isRunning()).isTrue();
    Session session = getCassandraSession();
    ResultSet result = session.execute("select * from user_group where group_id='group001'");
    Row row = result.iterator().next();
    assertEquals("User group 1", row.getString("group_name"));
  }

  @Test
  public void test_getCurrentUserGroup_success() {
    log.info("Instance of userGroupRepository {}", userGroupRepository.getClass().getName());

    UserGroupDto userGroupDto = webTestClient.get()
        .uri("/api/v1/usergroups")
        .header(HttpHeaders.AUTHORIZATION, "Bearer abc")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(UserGroupDto.class)
        .returnResult()
        .getResponseBody();

    Assertions.assertNotNull(userGroupDto);

  }

}

The injection code

@Autowired
private UserGroupRepository userGroupRepository;

Spring always inject Mockito bean for this repository, althought I try to use @EnableCassandraRepositories and @AutoConfigureDataCassandra but it seems not work.

My question is how to force Spring to inject the real implementation of my cassandra repository? Thanks.

Barcelona
  • 2,122
  • 4
  • 21
  • 42
  • Have you defined a MockBean of the repository type somewhere in your test configuration? Spring will only inject one, if you defined one, so if you don't want the MockBean, restrict its configuration (e.g. by saying, that it should only be active in certain profiles) – dunni Mar 21 '22 at 07:08
  • @dunni I updated the post. Currently I have seperate profile for unit test and integration test (test and integration profile) – Barcelona Mar 21 '22 at 07:19
  • Ok, with this configuration Spring will detect all MockBeans, because they are active in all profiles. Did you maybe want to use the `@Profile` annotation instead of `@ActiveProfiles` (https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html)? – dunni Mar 21 '22 at 07:21
  • @dunni I use ActiveProfiles for my integration test and Profile for TestConfig and it works. Why? – Barcelona Mar 21 '22 at 07:41
  • @dunni I was use wrong for Profile, it should be use for configuration. For test classes it should be ActiveProfiles Thanks for your help – Barcelona Mar 21 '22 at 07:55
  • As others have pointed out, this is unrelated to Testcontainers and is about the Spring-Boot usage. – Kevin Wittek Mar 23 '22 at 12:47

1 Answers1

0

The root cause is invalid usage of ActiveProfile and Profile in Spring. Update TestConfig to use @Profile instead of @ActiveProfiles.

Barcelona
  • 2,122
  • 4
  • 21
  • 42