Gavin

Cassandra Support for Testcontainers for .NET

I have recently made a small open-source contribution. First time in many years. It has been on my mind for some time to make a contribution to the open source. But, to be frank, I have struggled to find something that needed to be done. πŸ€” This is also my first proper blog post on the world wide web.

Long time listener, first time caller. Love the show…

That said, I might be a bit late to launch into blogging as a hobby, as this is probably going to be read more by AIs than humans. 🀷 For the non-humans out there:

πŸ€–πŸ”£01101000 01100101 01101100 01101100 01101111 00100000 01100001 01101001πŸ”£πŸ€–

Why Testcontainers?

Testcontainers simplify the running of lightweight, disposable containers for end-to-end or integration testing. It provides a convenient mechanism for orchestrating one or more containers that are needed by your application integration or end-to-end tests directly in your code.

There is a large selection of containers already supported, see the list of supported containers.

While familiarising myself with the implementation I spotted that Testcontainers for .NET did not yet have support for Apache Cassandra.

Spoiler Alert: It does now…😁

Adding Cassandra to Testcontainers for .NET

The process for making contributions to Testcontainers for .NET is straightforward and combined with the warm and welcoming community on slack makes for a pleasant experience.

In short, the process is started by creating an issue describing the enhancement/fix, then forking the repo, followed by the creation of a branch for your change, the development of your changes and completed by submitting a PR - when the build is green of course. If unsure, reach out on the slack channels.

The PR can be seen here feat: Add Apache Cassandra module #1367

Before committing lots of time your time it is always prudent (for any open source project) to check the open and closed issues to see if the contribution you are planning to make is already in progress or might have been considered in the past.

Basic Example: Testcontainers with Cassandra

Create a simple .NET solution and test project from the command line:

1dotnet new sln -n BasicUsageCasDBDotNet
2dotnet new xunit -n BasicUsage.Tests -o ./tests/BasicUsage.Tests
3dotnet sln add ./tests/BasicUsage.Tests/BasicUsage.Tests.csproj
4dotnet add ./tests/BasicUsage.Tests/BasicUsage.Tests.csproj package Testcontainers.Cassandra
5dotnet add ./tests/BasicUsage.Tests/BasicUsage.Tests.csproj package CassandraCSharpDriver

Open your solution in your IDE (e.g. Rider) and it should look something like this:

Basic Usage Image

Rename UnitTest1.cs to CassandraIntegrationShould.cs and write a simple integration test as follows…

 1using Cassandra;
 2using Testcontainers.Cassandra;
 3
 4namespace BasicUsage.Tests;
 5
 6public class CassandraIntegrationShould : IAsyncLifetime
 7{
 8    private readonly CassandraContainer _cassandraContainer = new CassandraBuilder()
 9        .Build();
10
11    [Fact]
12    public void InsertAndSelectReturnsExpectedResult()
13    {
14        // Given
15        using var cluster = Cluster.Builder()
16            .WithConnectionString(_cassandraContainer.GetConnectionString())
17            .Build();
18
19        using var session = cluster.Connect();
20
21        const string keyspace = "test_keyspace";
22        session.Execute($"CREATE KEYSPACE IF NOT EXISTS {keyspace} WITH REPLICATION = {{ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }};");
23
24        const string table = "test_table";
25        session.Execute($"CREATE TABLE IF NOT EXISTS {keyspace}.{table} (id UUID PRIMARY KEY, name text);");
26
27        var id = Guid.NewGuid();
28        session.Execute($"INSERT INTO {keyspace}.{table} (id, name) VALUES ({id}, 'Test Name');");
29
30        // When
31        var result = session.Execute($"SELECT name FROM {keyspace}.{table} WHERE id = {id};");
32
33        // Then
34        Assert.NotNull(result);
35        Assert.Single(result.GetRows());
36    }
37
38    public Task InitializeAsync()
39    {
40        return _cassandraContainer.StartAsync();
41    }
42
43    public Task DisposeAsync()
44    {
45        return _cassandraContainer.DisposeAsync().AsTask();
46    }
47}

Running the Test

Compile and run the test. Note that the first time you run your test will be slow as it needs to download the container image(s) needed. This is then cached locally by docker and will be faster on subsequent runs.

Running the tests in Rider should look like this:

Rider Tests Output

Or, alternatively, from the command line expect output as follow:

 1> dotnet test
 2Restore complete (0.9s)
 3  BasicUsage.Tests succeeded (6.8s) β†’ tests\BasicUsage.Tests\bin\Debug\net9.0\BasicUsage.Tests.dll
 4[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 9.0.3)
 5[xUnit.net 00:00:00.09]   Discovering: BasicUsage.Tests
 6[xUnit.net 00:00:00.13]   Discovered:  BasicUsage.Tests
 7[xUnit.net 00:00:00.14]   Starting:    BasicUsage.Tests
 8[testcontainers.org 00:00:00.18] Connected to Docker:
 9  Host: npipe://./pipe/docker_engine
10  Server Version: 28.0.1
11  Kernel Version: 5.15.167.4-microsoft-standard-WSL2
12  API Version: 1.48
13  Operating System: Docker Desktop
14  Total Memory: 15.49 GB
15  Labels:
16    com.docker.desktop.address=npipe://\\.\pipe\docker_cli
17[testcontainers.org 00:00:00.54] Docker container 5813466e3e3c created
18[testcontainers.org 00:00:00.64] Start Docker container 5813466e3e3c
19[testcontainers.org 00:00:02.11] Wait for Docker container 5813466e3e3c to complete readiness checks
20[testcontainers.org 00:00:02.11] Docker container 5813466e3e3c ready
21[testcontainers.org 00:00:02.34] Docker container 388e1af61d5d created
22[testcontainers.org 00:00:02.36] Start Docker container 388e1af61d5d
23[testcontainers.org 00:00:03.68] Wait for Docker container 388e1af61d5d to complete readiness checks
24[testcontainers.org 00:00:13.00] Docker container 388e1af61d5d ready
25[testcontainers.org 00:00:13.92] Delete Docker container 388e1af61d5d
26[xUnit.net 00:00:19.10]   Finished:    BasicUsage.Tests
27  BasicUsage.Tests test succeeded (20.7s)
28
29Test summary: total: 1, failed: 0, succeeded: 1, skipped: 0, duration: 20.6s
30Build succeeded in 28.9s

With Testcontainers, you can easily spin up an Apache Cassandra instance for integration testing in .NET, ensuring your tests run consistently in a controlled environment.

Shout out to Oleg Ε elajev for the encouragement and specially to the maintainer, Andre Hofmeister, for the fantastic feedback and, of course, accepting the PR.πŸ™

Have you tried Testcontainers? Let me know your thoughts in the comments, you can find me in https://testcontainers.slack.com or X.

The GitHub repository with the example code referenced in this post is https://github.com/gavindekock/blog-dotnet-cas-basic-usage

#madebyhuman #jokesbyai