Skip to content

Mock Cassandra (actually ScyllaDB) in C# unit tests

I have been lately working a lot with high throughput database called ScyllaDB. ScyllaDB is a Cassandra compatible, open source, nosql data store, which is accessed with Cassandra database drivers. It has higher throughput, lower latency and smaller memory footprint than Cassandra mainly because it is written in C++, not on top of JVM like Cassandra.

Unit testing classes that access database at somepoint can be a pain in the ass without proper abstraction. In dark ages of ADO it wasn’t that uncommon to find out few lines of code from project, that accessed database directly and made the whole function/class non-unit testable.

Cassandra C# driver however provides a decent abstraction and it supports mocking database operations like inserts and reads. It is just a bit hard at first.

Let’s assume that I have a class called CustomerRepository which implements ICustomerRepository. CustomerRepository has one method: GetCustomer(int id). That method reads customer from ScyllaDB (or Cassandra) and maps it into Customer entity. To unit test this mapping we need to write unit test against that GetCustomer method.

Code to unit test

public class Customer 
{
	public int Id { get; set; }
	public string Name { get; set; }
	public int Age { get; set; }
}

public interface ICustomerRepository
{
	Customer GetCustomer(int id);
}

public class CustomerRepository 
	: ICustomerRepository
{
	private readonly ISession session;
	
	public CustomerRepository(ISession session)
	{
		this.session = session;
	}

	public Customer GetCustomer(int id)
	{
		var statement = session.Prepare("SELECT * FROM Customers WHERE Id = ?");
		var boundStatement = statement.Bind(id);
		
		var dbCustomers = session.Execute(boundStatement);
		foreach(var row in dbCustomers)
		{
			return Map(row);
		}
		
		return null;
	}
	
	private Customer Map(Row row)
	{
		return new Customer 
		{
			Id = row.GetValue<int>("Id"),
			Name = row.GetValue<string>("Name"),
			Age = row.GetValue<int>("Age")
		};
	}
}
	

Map is the method that we want to unit test, but it is private, so we have to write test against GetCustomer method (which also contains some logic). We could separate map method from repository into own “Mapper” class, but that would still require mocking of Cassandra Row class.

First thing to notice is that we need to give some kind of ISession object to CustomerRepository constructor. Easiest way to get unit testable implementation is to use some kind of mocking library. In this example I’m using Moq, but there are others available also.

Unit test

First we have to mock ISession:

private Mock<ISession> sessionMock;

[SetUp]
public void Setup()
{
    sessionMock = new Mock<ISession>();

    sessionMock.Setup(_ => _.Prepare(It.IsAny<string>())).Returns(new PreparedStatement());
}

What we are setting up here is, that when Prepare method is invoked with any string argument, it will return the new PreparedStatement object, which is basically a dummy object which we don’t care about much, but to avoid null reference exceptions, we need to return something valid.

After binding our repository calls Execute method to fetch database rows from ScyllaDB. Mocking RowSet (which Execute returns) is sadly not that easy.

To mock RowSet we need to actually mock the GetEnumerator() method. We will create a list of mock objects and return thats enumerator when RowSet enumerator is read:

    public static class RowSetHelper
    {
        public static RowSet CreateMockRowSet(params Row[] rows)
        {
            var mockList = new List<Row>();
            mockList.AddRange(rows);

            var rowSet = new Mock<RowSet>();
            rowSet.Setup(m => m.GetEnumerator()).Returns(mockList.GetEnumerator());

            return rowSet.Object;
        }

        public static Mock<Row> CreateMockRow()
        {
            var rowMock = new Mock<Row>();
            rowMock.Setup(m => m.GetValue<int>("Id")).Returns(1);
            rowMock.Setup(m => m.GetValue<int>("Age")).Returns(1);
            rowMock.Setup(m => m.Getvalue<string>("Name")).Returns("Test");

            return rowMock;
        }
    }

Now we can put everything together into working unit test against repository.

 [Test]
 public void Repository_GeCustomer_ReturnsValidCustomer()
 {
     var dataRowMock = RowSetHelper.CreateMockRow();
     sessionMock.Setup(m => m.Execute(It.IsAny<IStatement>())).Returns(RowSetHelper.CreateMockRowSet(dataRowMock.Object));
     var repository = new CustomerRepository(_sessionMock.Object);

     var customer = repository.GetCustomer(1);

     customer.Should().NotBeNull("because get customer should return mock value from session.");
     customer.Id.Should().Be(1, "because repository has a customer with id 1");
}