Skip to content

jupiter-tools/spring-boot-extensions

Repository files navigation

JUnit5 Spring Boot Extensions

Database specific tests

jpa containers extension scheme

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 see at whole postgres annotation system at the next picture

postgres annotation system

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:

mysql annotation system

Messaging systems

messaging system annotations

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));
}
  1. queue name

  2. 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>

activemq annotations

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));
}
  1. queue name

  2. 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:

embedded web services tests

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}";
            }
        }
    }
 }
  1. bind the embedded server to an available TCP-port

  2. 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).