Spring boot : Testing Cassandra Repositories using Cassandra Unit

 

This short tutorial is about getting test classes running for the Cassandra repositories in a Spring boot application. The example provided uses Maven, Spring-data-cassandra and Cassandra-Unit. It also uses Lombok plugin, which is why you won’t see any getters/setters in these beans.

Step 1 : Add dependencies

Add these dependencies in your POM file –

[xml]
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.cassandraunit</groupId>
<artifactId>cassandra-unit-spring</artifactId>
<version>3.1.1.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.cassandraunit</groupId>
<artifactId>cassandra-unit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.cassandraunit</groupId>
<artifactId>cassandra-unit</artifactId>
<classifier>shaded</classifier>
<version>3.1.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.cassandraunit</groupId>
<artifactId>cassandra-unit-spring</artifactId>
<version>3.1.1.0</version>
</dependency>
<dependency>
<groupId>org.hectorclient</groupId>
<artifactId>hector-core</artifactId>
<version>2.0-0</version>
<scope>test</scope>
</dependency>

[/xml]

Step 2 : Deciding on alternate port for test Cassandra and creating a corresponding YAML file.

  1. Download this file : another-cassandra.yaml
  2. Edit the property “native_transport_port” to the port you need the test (embedded) Cassandra to listen to. The port configured in the file is 9152, which can be used unchanged.
  3. Add the file to class-path (resources folder).

Step 3 : Write a simple test to check if the embedded Cassandra starts up fine.

[java]
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;

import java.io.IOException;

import me.prettyprint.cassandra.service.CassandraHostConfigurator;
import me.prettyprint.hector.api.Cluster;
import me.prettyprint.hector.api.ddl.KeyspaceDefinition;
import me.prettyprint.hector.api.factory.HFactory;

import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.thrift.transport.TTransportException;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;

public class EmbededCassandraTest {
@Before
public void before() throws TTransportException, IOException, InterruptedException, ConfigurationException {
EmbeddedCassandraServerHelper.startEmbeddedCassandra("another-cassandra.yaml", 20000);
}

@Test
public void shouldHaveAnEmbeddedCassandraStartOn9175Port() throws Exception {
Cluster cluster = HFactory.getOrCreateCluster("TestCluster", new CassandraHostConfigurator("localhost:9175"));
assertThat(cluster.getConnectionManager().getActivePools().size(), is(1));
KeyspaceDefinition keyspaceDefinition = cluster.describeKeyspace("system");
assertThat(keyspaceDefinition, notNullValue());
assertThat(keyspaceDefinition.getReplicationFactor(), is(1));

}
@AfterClass
public static void cleanCassandra() {
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}
}

[/java]

Step 4: Create or Edit the application-test.properties for setting the embedded cassandra server settings for the test profile.

Add these properties to application-test.properties. Note that these properties would be picked by the config file we will add in steps further down.

[python]
cassandra.hosts=localhost
cassandra.port=9152
cassandra.keyspace=yourkeyspace
[/python]

Step 5: Spring boot Java Config files

These are the main and test Cassandra config classes for this example –

Main Cassandra Config:

[java]
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.CassandraClusterFactoryBean;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.config.java.AbstractCassandraConfiguration;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;

@Configuration
@EnableCassandraRepositories("package.of.your.repositories")
public class CassandraConfig extends AbstractCassandraConfiguration {

@Value("${cassandra.hosts}")
String cassandraHosts;
@Value("${cassandra.port}")
String cassandraPort;
@Value("${cassandra.keyspace}")
String cassandraKeySpace;

@Override
public String getKeyspaceName() {
return cassandraKeySpace;
}

@Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster =
new CassandraClusterFactoryBean();
cluster.setContactPoints(cassandraHosts);
cluster.setPort(Integer.parseInt(cassandraPort));
return cluster;
}

@Override
public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
@Override
public String[] getEntityBasePackages() {
return new String[]{
"package.of.your.domains"};
}
}
[/java]

Test Cassandra Config :

[java]
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.test.context.ActiveProfiles;

@Configuration
@ActiveProfiles("test")
@PropertySource( {"classpath:application.properties",
"classpath:application-test.properties"})
public class CassandraTestConfig extends CassandraConfig {

}
[/java]

Step 6 : Write the abstract test class

Write this abstract class which the repository test classes can inherit, and which will abstract out the Cassandra configurations :

[java]

import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Session;
import com.mycompany.config.CassandraTestConfig;
import lombok.extern.slf4j.XSlf4j;
import org.apache.thrift.transport.TTransportException;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.springframework.context.annotation.Import;

import java.io.IOException;
import java.util.Properties;

@Import(CassandraTestConfig.class)
@XSlf4j
public abstract class AbstractRepositoryTest {

@BeforeClass
public static void initCassandra() {
try {
Properties prop = new Properties();
prop.load(AbstractRepositoryTest.class.getClassLoader().getResourceAsStream("application-test.properties"));
String cassandraHosts = prop.getProperty("cassandra.hosts");
String cassandraPort = prop.getProperty("cassandra.port");

EmbeddedCassandraServerHelper.startEmbeddedCassandra("another-cassandra.yaml", 20000);
log.info("Connect to embedded db");
Cluster cluster = Cluster.builder().addContactPoints(cassandraHosts).withPort(Integer.parseInt(cassandraPort)).build();
Session session = cluster.connect();

log.info("Initialize keyspace");
session.execute("create keyspace \"mykeyspace\" WITH replication = {‘class’:’SimpleStrategy’, ‘replication_factor’ : 3};");
session.execute("use \"mykeyspace\";");
} catch (TTransportException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

@Before
public void initTest() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
@AfterClass
public static void cleanCassandra() {
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}

}

[/java]

Step 7 :  Domain Object/Entity

[java]
import com.datastax.driver.core.utils.UUIDs;
import lombok.Data;
import org.springframework.cassandra.core.Ordering;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;

import java.io.Serializable;
import java.util.UUID;

@Data
@Table(value = "sampleEntity")
public class SampleEntity implements Serializable {
@PrimaryKeyColumn(name = "id", ordinal = 0, type = PrimaryKeyType.PARTITIONED, ordering = Ordering.DESCENDING)
private UUID id;
private String text;
}

[/java]

Step 8 :  Repository for ze entity

[java]

import org.springframework.data.cassandra.repository.CassandraRepository;

import java.util.List;
import java.util.UUID;

public interface SampleEntityRepository extends CassandraRepository<SampleEntity> {
SampleEntity findById(UUID id);
}
[/java]

Step 9 : The repository test, gentlemen !

[java]

import com.datastax.driver.core.utils.UUIDs;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;
import java.util.UUID;

@RunWith(SpringJUnit4ClassRunner.class)
public class SampleEntityRepoTest extends AbstractRepositoryTest {

@Autowired
SampleEntityRepository sampleEntityRepository;

@Test
public void saveOneTest() {
UUID id = UUIDs.timeBased();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(id);
sampleEntity.setText("Hi");
sampleEntity = sampleEntityRepository.save(sampleEntity);
SampleEntity retrievedSampleEntity = sampleEntityRepository.findById(id);
Assert.assertEquals("Hi", sampleEntity.getText());
}
//other tests…
}

[/java]

Done.
Run the test class to confirm the setup works (or not – which case you are on your own).

Kj
Kj [/.] Sreekumar programs computers as a hobby and profession. Into programming from his school days, Sree uses Codemarvels to key in facts and fixes he finds interesting while working on different projects. Some of the articles here give away a few shades of his philosophical leanings too.

Leave a Comment

Your email address will not be published. Required fields are marked *