JUnit5 Spring Boot Extensions
Database specific tests
TraceSql Extension
Provide you an ability to check SQL statement execution after tests. You need to add the next dependency:
<dependency>
<groupId>com.jupiter-tools</groupId>
<artifactId>spring-test-jpa</artifactId>
<version>0.2</version>
</dependency>
And now, you can assert a count of insert operations after save an entity:
@ExtendWith(TraceSqlExtension.class)
@ExtendWith(SpringExtension.class)
@SpringBootTest
class TraceSqlExtensionTest {
@Autowired
private FooRepository fooRepository;
@Test
void testInsert() {
// Act
fooRepository.save(new Foo("any data"));
// Assert
AssertSqlCount.assertInsertCount(1);
}
}
Also, you can use annotation on the test case to assert the expected count of executed queries:
@Test
@ExpectedSqlQuery(type = INSERT, count = 1)
void testInsert() {
fooRepository.save(new Foo("data"));
}
@Test
@ExpectedSqlQuery(type = SELECT, count = 1)
void testSelect() {
fooRepository.findAll();
}
PostgreSQL Extension
To run the docker postgres image by the test-containers library in your integration test, you can use PostgresTcExtension.
<dependency>
<groupId>com.jupiter-tools</groupId>
<artifactId>spring-test-postgres</artifactId>
<version>0.2</version>
</dependency>
Let’s try to execute a stored procedure specific to PostgreSQL:
@DataJpaTest
@ExtendWith(SpringExtension.class)
@ExtendWith(PostgresTcExtension.class)
@ExtendWith(TraceSqlExtension.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class PostgresTcExtensionTest {
@PersistenceContext
private EntityManager entityManager;
@Test
@Sql("/stored_functions/test_func.sql")
void testStoredFunc() {
// Arrange
StoredProcedureQuery query = entityManager.createStoredProcedureQuery("rnd");
// Act
query.execute();
// Assert
List resultList = query.getResultList();
int rnd = (int) resultList.get(0);
Assertions.assertThat(rnd).isEqualTo(123);
}
}
This library provides a wide system of meta-annotations to simplified writing integration test’s configuration.
For example, you can write:
@EnablePostgresDataTest
class EnablePostgresDataTestTest {
...
}
instead of:
@DataJpaTest
@ExtendWith(SpringExtension.class)
@ExtendWith(PostgresTcExtension.class)
@ExtendWith(TraceSqlExtension.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class PostgresTcExtensionTest {
...
}
You can build a necessary test configuration by using annotations which you need. Meta-annotations is a beautiful mechanism which will you a making configuration in a declarative style.
MySql Extension
MySqlTcExtension runs the mysql docker image and set spring properties in the configuration to use this datasource in tests.
<dependency>
<groupId>com.jupiter-tools</groupId>
<artifactId>spring-test-mysql</artifactId>
<version>0.2</version>
</dependency>
You can use this extension by the applying of EnableMySqlTestContainersExtension
annotation or you can use @EnableMySqlDataTest
to write a test with the DataJpa context configuration:
@EnableMySqlDataTest
class EnableMySqlDataTestTest {
@Autowired
private FooRepository repository;
@Test
@Commit
@DataSet(cleanBefore = true, cleanAfter = true)
@ExpectedDataSet(value = "/datasets/expected.json", ignoreCols = "ID")
void testCreate() throws Exception {
repository.saveAndFlush(new Foo("any data"));
}
}
As well as for the PostgreSQL in this library there is a system of meta-annotations for the MySql:
Messaging systems
RabbitMq Extension
RabbitMqTcExtension
runs the RabbitMq docker image by the TestContainers library and configure SpringBoot properties to work with this container.
<dependency>
<groupId>com.jupiter-tools</groupId>
<artifactId>spring-test-rabbitmq</artifactId>
<version>0.2</version>
</dependency>
Now we can run RabbitMq in tests and send a message in a real queue:
@SpringBootTest
@ExtendWith(SpringExtension.class)
@ExtendWith(RabbitMqTcExtension.class)
class EnableRabbitMqTestTest {
@Autowired
private AmqpTemplate amqpTemplate;
@Test
void testSend() {
amqpTemplate.convertAndSend("test-queue", "123");
...
}
}
As well as with database specific tests, in this case, you can use meta-annotation to write tests more pragmatic:
@EnableRabbitMqTest
class EnableRabbitMqTestTest {
...
}
Also, you can assert the sending of messages in the selected queue:
@EnableRabbitMqTest
public class ExpectedMessageTest {
@Autowired
private AmqpTemplate amqpTemplate;
@Test
@ExpectedMessage(queue = "test-queue", message = "123")
void testSend() throws InterruptedException {
amqpTemplate.convertAndSend("test-queue", "123");
}
}
Also, you can assert the receiving of multiple messages:
@Autowired
private AmqpTemplate amqpTemplate;
@Test
@ExpectedMessages(queue = "test-queue", (1)
messagesFile = "/datasets/expected_messages.json") (2)
void testSendListOfMessages() {
// first type:
amqpTemplate.convertAndSend("test-queue", new Foo("123"));
// second type:
amqpTemplate.convertAndSend("test-queue", new Bar("AAA",1));
amqpTemplate.convertAndSend("test-queue", new Bar("BBB",2));
amqpTemplate.convertAndSend("test-queue", new Bar("CCC",3));
}
-
queue name
-
file with expected messages in JSON format
Content of the expected_messages.json
:
{
"com.jupiter.tools.spring.test.rabbitmq.extension.pojo.Foo": [
{
"value":"123"
}
],
"com.jupiter.tools.spring.test.rabbitmq.extension.pojo.Bar":[
{
"name":"AAA",
"count":1
},
{
"name":"BBB",
"count":2
},
{
"name":"CCC",
"count":3
}
]
}
ActiveMq Extension
You can run the ActiveMq docker image by the using of EnableActiveMqTestContainers
annotation.
You need to use the next dependency:
<dependency>
<groupId>com.jupiter-tools</groupId>
<artifactId>spring-test-activemq</artifactId>
<version>0.2</version>
</dependency>
If you need to check a sending of messages then you can use the ExpectedMessage
annotation:
@SpringBootTest
@EnableActiveMqTest
public class SendMessageTest {
@Autowired
private JmsTemplate jmsTemplate;
@Test
@ExpectedMessage(queue = "test-queue", message = "123")
void testSend() {
jmsTemplate.convertAndSend("test-queue", "123");
}
@TestConfiguration
public static class TestConfig {
@Bean
public Queue testQueue() {
return new Queue("test-queue");
}
}
}
Also, you can assert the receiving of multiple messages:
@Test
@ExpectedMessages(queue = "test-queue", (1)
messagesFile = "/datasets/expected_messages.json") (2)
void testSendListOfMessages() {
// first type:
jmsTemplate.convertAndSend("test-queue", new Foo("123"));
// second type:
jmsTemplate.convertAndSend("test-queue", new Bar("AAA",1));
jmsTemplate.convertAndSend("test-queue", new Bar("BBB",2));
jmsTemplate.convertAndSend("test-queue", new Bar("CCC",3));
}
-
queue name
-
file with expected messages in JSON format
Content of the expected_messages.json
:
{
"com.jupiter.tools.spring.test.activemq.extension.expected.Foo": [
{
"value":"123"
}
],
"com.jupiter.tools.spring.test.activemq.extension.expected.Bar":[
{
"name":"AAA",
"count":1
},
{
"name":"BBB",
"count":2
},
{
"name":"CCC",
"count":3
}
]
}
Embedded Web Server
Let’s consider the next microservice based application:
You can test inter-service communication by the running an embedded web server with a mocked external controller and send HTTP requests to this server.
<dependency>
<groupId>com.jupiter-tools</groupId>
<artifactId>spring-test-web</artifactId>
<version>0.2</version>
</dependency>
Let’s test requesting to the template-service by the using of embedded server:
@EnableEmbeddedWebServerTest (1)
@RedirectRibbonToEmbeddedWebServer("template-service") (2)
class RedirectRibbonExtensionTest {
@Autowired
private RestTemplate restTemplate;
@Test
void testRedirect() {
// Act
String result = restTemplate.getForObject("http://template-service/templates/{template}",
String.class,
"balance-template");
// Assert
assertThat(result).isEqualTo("{user} balance = {value}");
}
@TestConfiguration
public static class TestCfg {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@RestController
@RequestMapping("/templates")
public class TestApi {
@GetMapping("/{template}")
public String getLength(@PathVariable("template") String template) {
return "{user} balance = {value}";
}
}
}
}
-
bind the embedded server to an available TCP-port
-
resolve the client name("template-service") to an embedded server url
If you want to run different web servers in one test suite then you need to use a different port to each server. And you need to be sure that selected port is available.
Annotation EnableEmbeddedWebServerTest
bind a random available TCP port to the server.port property of the Spring Framework.
RedirectRibbonToEmbeddedWebServer
redirects all requests from any ribbon clients to embedded server, by default (if you don’t set the value of this annotation).