1. Обзор
Apache Cassandra — это мощная распределенная база данных NoSQL с открытым исходным кодом. В предыдущем уроке мы рассмотрели некоторые основы работы с Cassandra и Java .
В этом руководстве мы будем основываться на предыдущем и узнаем, как писать надежные автономные модульные тесты с использованием CassandraUnit .
Во-первых, мы начнем с того, как установить и настроить последнюю версию CassandraUnit. Затем мы рассмотрим несколько примеров того, как мы можем написать модульные тесты, которые не полагаются на работающий внешний сервер базы данных.
И, если вы используете Cassandra в производственной среде, вы определенно можете упростить запуск и обслуживание собственного сервера и вместо этого использовать базу данных Astra , которая представляет собой облачную базу данных, построенную на Apache Cassandra.
2. Зависимости
Конечно, нам нужно добавить стандартный Java-драйвер Datastax для Apache Cassandra в наш 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 :
CREATE TABLE person(
id varchar,
name varchar,
PRIMARY KEY(id));
INSERT INTO person(id, name) values('1234','ForEach');
INSERT INTO person(id, name) values('5678','Michael');
Как мы увидим, CassandraUnit предлагает несколько вариантов, помогающих нам писать тесты, но в основе их лежит несколько простых концепций, которые мы будем повторять:
- Во-первых, мы запустим встроенный сервер Cassandra, который работает в памяти внутри нашей JVM.
- Затем мы загрузим наш набор данных о людях в работающий встроенный экземпляр.
- Наконец, мы запустим простой запрос, чтобы убедиться, что наши данные были загружены правильно.
В заключение этого раздела несколько слов о тестировании. В общем, при написании чистых модульных или интеграционных тестов мы не должны зависеть от внешних служб, которые мы не можем контролировать или которые могут внезапно перестать работать . Это может отрицательно сказаться на результатах наших тестов.
Точно так же, если мы зависим от внешней службы, в данном случае от работающей базы данных Cassandra, мы, скорее всего, не сможем настроить ее, контролировать и отключить так, как мы хотим в наших тестах.
4. Тестирование с использованием нативного подхода
Давайте начнем с рассмотрения того, как использовать собственный API, поставляемый с CassandraUnit. Во-первых, мы продолжим и определим наш модульный тест и настройку теста:
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("ForEach"));
}
Как мы видим, выполнение простого запроса подтверждает, что наш тест работает правильно. Потрясающий! Теперь у нас есть способ писать автономные, независимые модульные тесты, используя базу данных Cassandra в памяти .
Наконец, когда мы разорвем наш тест, мы очистим наш встроенный экземпляр:
@After
public void tearDown() throws Exception {
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}
Выполнение этого приведет к удалению всех существующих пространств ключей, кроме системного
пространства ключей.
5. Тестирование с использованием тестового примера CassandraUnit Abstract 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("ForEach"));
}
}
На этот раз, расширив класс AbstractCassandraUnit4CQLTestCase
, все, что нам нужно сделать, это переопределить метод getDataSet()
, который возвращает CQLDataSet
, который мы хотим загрузить.
Еще одно тонкое отличие состоит в том, что в нашем тесте нам нужно вызвать getSession()
, чтобы получить доступ к драйверу Java Cassandra.
6. Тестирование с использованием правила CassandraCQLUnit
JUnit
Если мы не хотим заставлять наши тесты расширять AbstractCassandraUnit4CQLTestCase,
то, к счастью, CassandraUnit также предоставляет стандартное правило JUnit :
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
. Это правило будет подготавливать и управлять жизненным циклом нашего сервера Cassandra.
7. Работа с пружиной
Обычно мы можем интегрировать Cassandra со Spring в наши проекты. К счастью, CassandraUnit также поддерживает работу с Spring TestContext Framework.
Чтобы воспользоваться этой поддержкой, нам нужно добавить в наш проект зависимость Maven cassandra-unit-spring :
<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("ForEach"));
}
}
Давайте пройдемся по ключевым частям нашего теста. Во-первых, мы начнем с украшения нашего тестового класса двумя довольно стандартными аннотациями, связанными со Spring:
- Аннотация @RunWith
(SpringJUnit4ClassRunner.class)
гарантирует, что наш тест встраиваетTestContextManager
Spring в наш тест, предоставляя нам доступ к SpringApplicationContext.
- Мы также указываем собственный
TestExecutionListener,
называемыйCassandraUnitTestExecutionListener,
который отвечает за запуск и остановку нашего сервера и поиск других аннотаций CassandraUnit.
Здесь наступает решающая часть; мы используем аннотацию @EmbeddedCassandra
для внедрения экземпляра встроенного сервера Cassandra в наши тесты . Кроме того, есть несколько доступных свойств, которые мы можем использовать для дальнейшей настройки встроенного сервера базы данных:
конфигурация
— другой файл конфигурации CassandraclusterName
— имя кластераhost
— хост нашего кластераport
— порт, используемый нашим кластером
Здесь мы упростили ситуацию, выбрав значения по умолчанию, исключив эти свойства из нашего объявления.
В качестве последней части головоломки мы используем аннотацию @CassandraDataSet
для загрузки того же набора данных CQL, который мы видели ранее. Как и раньше, мы можем отправить запрос, чтобы проверить правильность содержимого нашей базы данных.
8. Заключение
В этой статье мы узнали о нескольких способах работы с CassandraUnit для написания автономных модульных тестов с использованием встроенного экземпляра Apache Cassandra. Мы также обсудили, как мы можем работать с Spring из наших модульных тестов.
Как всегда, полный исходный код статьи доступен на GitHub .