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 –

        <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>

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.

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();
    }
}

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.

cassandra.hosts=localhost
cassandra.port=9152
cassandra.keyspace=yourkeyspace

Step 5: Spring boot Java Config files

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

Main Cassandra Config:

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"};
    }
}

Test Cassandra Config :

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 {

}

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 :


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();
    }

}

Step 7 :  Domain Object/Entity

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;
}

Step 8 :  Repository for ze entity


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);
}

Step 9 : The repository test, gentlemen !


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...
}

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

Leave a Reply

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

*

* Copy This Password *

* Type Or Paste Password Here *

43,751 Spam Comments Blocked so far by Spam Free Wordpress

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>