Unit Testing Using Embedded Cassandra
Cassandra is column oriented database which comes under category of NoSQL. There are various companies which uses Cassandra in many use of their use cases.
Few use cases are
- Saving product catalog
- Saving Time Series Data
We all do development in companies where we write codes but that is useless if we don't write test cases also known as unit testing. Testing not only makes things easy for us when it comes to enhancement or when we are building new feature.
Best use case of testing can be when you are assigned to new project which do not have test cases so How you will know your modification / enhancement in code won't affect other features / functionality. Generally We expect others to write test cases that time but we ourselves avoid test cases because of thinking that it is responsibility of tester. It's responsibility of developer to write unit test cases. Unit test cases are one way to validate if all functionalities are working fine.
Few days back I was working for one of my client and he was using Cassandra in one of his application. He was not able to connect to remote Cassandra server neither he was able to install due to permission issues on windows. He was stuck and didn't know what to do.
In last article I wrote about using Embedded Kafka which makes testing easy without setting up Kafka and Zookeeper. I found out something similar for cassandra known as Embedded Cassandra. It is easy to code and test functionality in any application which uses Cassandra.
CassandraUnit provides exactly what is required here. It starts Embedded Cassandra before test methods, has the ability to create structure and allows the developer to run CQL queries without constantly starting the application
I will write steps to use Embedded Cassandra
- Maven Dependency
<dependency>
<groupId>org.cassandraunit</groupId>
<artifactId>cassandra-unit-spring</artifactId>
<version>3.1.3.2</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-shaded</artifactId>
<version>3.1.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hectorclient</groupId>
<artifactId>hector-core</artifactId>
<version>2.0-0</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
2. Setting up Spring Boot Application
This will include a sample Spring Boot application which used Spring data cassandra. Major components are Cassandra Connection, Session , Repository and Models. I am assuming you already know or you can directly refer project which is on GitHub.
For testing purpose I have created an Order Table which will save Order id, Amount & Discount.
Order Class
package com.casandra.test.domain.cassandra;
import java.io.Serializable;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
import lombok.Getter;
import lombok.Setter;
@Table("ORDER_DATA")
@Getter
@Setter
public class Order implements Serializable {
private static final long serialVersionUID = 2404160767202085990L;
@Id
@PrimaryKeyColumn(name = "orderID", type = PrimaryKeyType.PARTITIONED, ordinal = 0)
private String orderID;
private Float amount;
private Float discount;
}
Order Repository
package com.casandra.test.repository;
import java.util.List;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.Query;
import org.springframework.stereotype.Repository;
import com.casandra.test.domain.cassandra.Order;
@Repository
public interface OrderRepository extends CassandraRepository<Order> {
@Query("select * from ORDER_DATA where orderID = ?0")
public List<Order> findByOrderId(String orderID);
}
Order Controller
package com.casandra.test.controller;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.casandra.test.model.SaveRequest;
import com.casandra.test.service.OrderService;
@RestController
public class OrderController {
private static Logger log = Logger.getLogger(OrderController.class);
@Autowired
private OrderService orderService;
@RequestMapping(value = "/order/save", method = RequestMethod.POST)
public boolean cprEligibilitycheck(@RequestBody SaveRequest request) {
log.info("inside save request controller.");
return orderService.save(request);
}
}
Below is controller request which will save data in Cassandra.
POST /order/save HTTP/1.1
Host: localhost:8055
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 771e2763-2574-b75b-b4c7-2c568d7a4cfe
{
"orderId":"1212",
"amount":123.12,
"discount":12.12
}
3. Writing Test cases for Repository
To test cassandra repository we have to run Embedded Cassandra.
@BeforeClass
public static void startCassandraEmbedded()
throws InterruptedException, TTransportException, ConfigurationException, IOException {
EmbeddedCassandraServerHelper.startEmbeddedCassandra();
final Cluster cluster = Cluster.builder().addContactPoints("https://www.garudax.id/redir/invalid-link-page?url=127%2e0%2e0%2e1").withPort(9142).build();
LOGGER.info("Server Started at https://www.garudax.id/redir/invalid-link-page?url=127%2e0%2e0%2e1%3A9142... ");
final Session session = cluster.connect();
session.execute(KEYSPACE_CREATION_QUERY);
session.execute(KEYSPACE_ACTIVATE_QUERY);
LOGGER.info("KeySpace created and activated.");
Thread.sleep(5000);
}
Once this is done we have to follow existing approach where we enable repositories and provide entity packages path to create tables in Cassandra. Basically once embedded Cassandra starts we have to create necessary database structure which is needed for our test cases.
@Configuration
@EnableCassandraRepositories(basePackages = { "com.casandra.test.repository" })
public static class DaoConfiguration {
@Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints("https://www.garudax.id/redir/invalid-link-page?url=127%2e0%2e0%2e1");
cluster.setPort(9142);
return cluster;
}
@Bean
public CassandraMappingContext mappingContext() throws ClassNotFoundException {
BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(cluster().getObject(), KEYSPACE));
mappingContext.setInitialEntitySet(
CassandraEntityClassScanner.scan(new String[] { "com.casandra.test.domain.cassandra" }));
return mappingContext;
}
@Bean
public CassandraConverter converter() throws ClassNotFoundException {
return new MappingCassandraConverter(mappingContext());
}
@Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(KEYSPACE);
session.setConverter(converter());
session.setSchemaAction(SchemaAction.CREATE_IF_NOT_EXISTS);
return session;
}
@Bean
public CassandraOperations cassandraTemplate() throws Exception {
return new CassandraTemplate(session().getObject());
}
}
Complete code can be found at GIT HUB CASSANDRA UNIT TESTING
Issues :
- I observed one issue with windows when I tried to run test case , embedded Cassandra was not able start. Let me know if You are able to fix that. In mac it was working fine.
Happy Coding!!!