Cassandra单元测试教程

2023/05/09

1. 概述

Apache Cassandra是一个功能强大的开源NoSQL分布式数据库。在之前的教程中,我们了解了如何使用Cassandra和Java的一些基础知识。

在本教程中,我们将在前一个教程的基础上学习如何使用CassandraUnit编写可靠、独立的单元测试

首先,我们将了解如何设置和配置最新版本的CassandraUnit。然后我们将探讨几个示例,说明如何编写不依赖于运行的外部数据库服务器的单元测试。

而且,如果你在生产环境中运行Cassandra,你绝对可以省去运行和维护自己服务器的复杂性,转而使用Astra数据库,这是一个基于Apache Cassandra构建的基于云的数据库

2. 依赖项

当然,我们需要将Apache Cassandra的标准Datastax Java驱动程序添加到我们的pom.xml中:

<dependency>
    <groupId>com.datastax.oss</groupId>
    <artifactId>java-driver-core</artifactId>
    <version>4.13.0</version>
</dependency>

为了使用嵌入式数据库服务器测试我们的代码,我们还应该将cassandra-unit依赖项添加到我们的pom.xml中:

<dependency>
    <groupId>org.cassandraunit</groupId>
    <artifactId>cassandra-unit</artifactId>
    <version>4.3.1.0</version>
    <scope>test</scope>
</dependency>

现在我们已经配置了所有必要的依赖项,我们可以开始编写单元测试了。

3. 入门

在本教程中,我们测试的重点将是通过简单的CQL脚本控制的简单person表:

CREATE TABLE person(
    id varchar,
    name varchar,
    PRIMARY KEY(id));

INSERT INTO person(id, name) values('1234','Eugen');
INSERT INTO person(id, name) values('5678','Michael');

正如我们将要看到的,CassandraUnit提供了多种变体来帮助我们编写测试,但它们的核心是我们将不断重复的几个简单概念:

  • 首先,我们将启动一个嵌入式Cassandra服务器,它在我们的JVM内存中运行。
  • 然后我们将person数据集加载到正在运行的嵌入式实例中。
  • 最后,我们将启动一个简单的查询来验证我们的数据是否已正确加载。

在结束本节之前,简要介绍一下测试。一般来说,在编写干净的单元或集成测试时,我们不应该依赖我们可能无法控制或可能突然停止工作的外部服务。这可能会对我们的测试结果产生不利影响。

同样,如果我们依赖于外部服务,在本例中是一个正在运行的Cassandra数据库,我们可能无法按照我们希望的测试方式设置、控制和拆除它。

4. 使用原生方法进行测试

让我们先来看看如何使用CassandraUnit自带的原生API。首先,我们将继续定义我们的单元测试和测试设置:

public class NativeEmbeddedCassandraUnitTest {

    private CqlSession session;

    @Before
    public void setUp() throws Exception {
        EmbeddedCassandraServerHelper.startEmbeddedCassandra();
        session = EmbeddedCassandraServerHelper.getSession();
        new CQLDataLoader(session).load(new ClassPathCQLDataSet("people.cql", "people"));
    }
}

让我们来看看我们测试设置的关键部分。首先,我们启动一个嵌入式Cassandra服务器。为此,我们所要做的就是调用startEmbeddedCassandra()方法

这将使用固定端口9142启动我们的数据库服务器:

11:13:36.754 [pool-2-thread-1] INFO  o.apache.cassandra.transport.Server
  - Starting listening for CQL clients on localhost/127.0.0.1:9142 (unencrypted)...

如果我们更喜欢使用随机可用的端口,我们可以使用提供的Cassandra YAML配置文件:

EmbeddedCassandraServerHelper
    .startEmbeddedCassandra(EmbeddedCassandraServerHelper.CASSANDRA_RNDPORT_YML_FILE);

同样,我们也可以在启动服务器时传入自己的YAML配置文件。当然,这个文件需要在我们的类路径中。

接下来,我们可以继续将我们的people.cql数据集加载到我们的数据库中。为此,我们使用ClassPathCQLDataSet类,该类接收数据集位置和可选的键空间名称

现在我们已经加载了一些数据,并且我们的嵌入式服务器已启动并运行,我们可以继续编写一个简单的单元测试:

@Test
public void givenEmbeddedCassandraInstance_whenStarted_thenQuerySuccess() throws Exception {
    ResultSet result = session.execute("select * from person WHERE id=1234");
    assertThat(result.iterator().next().getString("name"), is("Eugen"));
}

正如我们所见,执行一个简单的查询可以确认我们的测试工作正常。我们现在有一种方法可以使用内存中的Cassandra数据库编写独立的单元测试

最后,当我们拆除测试时,我们将清理我们的嵌入式实例:

@After
public void tearDown() throws Exception {
    EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}

执行此操作将删除除system键空间之外的所有现有键空间。

5. 使用CassandraUnit抽象JUnit测试用例进行测试

为了帮助简化我们在上一节中看到的示例,CassandraUnit提供了一个抽象测试用例类AbstractCassandraUnit4CQLTestCase,它负责我们之前看到的设置和拆卸:

public class AbstractTestCaseWithEmbeddedCassandraUnitTest extends AbstractCassandraUnit4CQLTestCase {

    @Override
    public CQLDataSet getDataSet() {
        return new ClassPathCQLDataSet("people.cql", "people");
    }

    @Test
    public void givenEmbeddedCassandraInstance_whenStarted_thenQuerySuccess() throws Exception {
        ResultSet result = this.getSession().execute("select * from person WHERE id=1234");
        assertThat(result.iterator().next().getString("name"), is("Eugen"));
    }
}

这一次,通过扩展AbstractCassandraUnit4CQLTestCase类,我们需要做的就是覆盖getDataSet()方法,该方法返回我们希望加载的CQLDataSet。

另一个细微差别是,根据我们的测试,我们需要调用getSession()来访问Cassandra Java驱动程序。

6. 使用CassandraCQLUnit JUnitRule进行测试

如果我们不想强制我们的测试扩展AbstractCassandraUnit4CQLTestCase,那么幸运的是CassandraUnit还提供了一个标准的JUnit Rule

public class JUnitRuleWithEmbeddedCassandraUnitTest {

    @Rule
    public CassandraCQLUnit cassandra = new CassandraCQLUnit(new ClassPathCQLDataSet("people.cql", "people"));

    @Test
    public void givenEmbeddedCassandraInstance_whenStarted_thenQuerySuccess() throws Exception {
        ResultSet result = cassandra.session.execute("select * from person WHERE id=5678");
        assertThat(result.iterator().next().getString("name"), is("Michael"));
    }
}

我们所要做的就是在我们的测试中声明一个CassandraCQLUnit字段,这是一个标准的JUnit @Rule。此Rule将准备和管理我们的Cassandra服务器的生命周期

7. 使用Spring

通常我们可能会在我们的项目中将Cassandra与Spring集成。幸运的是,CassandraUnit还为使用Spring TestContext框架提供了支持。

要利用此支持,我们需要将cassandra-unit-spring Maven依赖项添加到我们的项目中:

<dependency>
    <groupId>org.cassandraunit</groupId>
    <artifactId>cassandra-unit-spring</artifactId>
    <version>4.3.1.0</version>
    <scope>test</scope>
</dependency>

现在我们可以访问一些可以在测试中使用的注解和类。让我们继续编写一个使用最基本的Spring配置的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({ CassandraUnitTestExecutionListener.class })
@CassandraDataSet(value = "people.cql", keyspace = "people")
@EmbeddedCassandra
public class SpringWithEmbeddedCassandraUnitTest {

    @Test
    public void givenEmbeddedCassandraInstance_whenStarted_thenQuerySuccess() throws Exception {
        CqlSession session = EmbeddedCassandraServerHelper.getSession();

        ResultSet result = session.execute("select * from person WHERE id=1234");
        assertThat(result.iterator().next().getString("name"), is("Eugen"));
    }
}

让我们来看看测试的关键部分。首先,我们用两个非常标准的Spring相关注解来装饰我们的测试类:

  • @RunWith(SpringJUnit4ClassRunner.class)注解将确保我们的测试将Spring的TestContextManager嵌入到我们的测试中,使我们能够访问Spring ApplicationContext。
  • 我们还指定了一个自定义的TestExecutionListener,称为CassandraUnitTestExecutionListener,它负责启动和停止我们的服务器并查找其他CassandraUnit注解。

关键的部分来了;我们使用@EmbeddedCassandra注解将嵌入式Cassandra服务器的实例注入到我们的测试中。此外,我们可以使用几个可用的属性来进一步配置嵌入式数据库服务器:

  • configuration:一个不同的Cassandra配置文件
  • clusterName:集群的名称
  • host:集群的主机
  • port:集群使用的端口

我们在这里保持简单,通过从声明中省略这些属性来选择默认值。

对于拼图的最后一部分,我们使用@CassandraDataSet注解来加载我们之前看到的相同CQL数据集。与以前一样,我们可以发送一个查询来验证我们数据库的内容是否正确。

8. 总结

在本文中,我们学习了几种可以使用CassandraUnit的方法,以使用Apache Cassandra的嵌入式实例编写独立的单元测试。我们还讨论了如何从我们的单元测试中使用Spring。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章