[JAVA] Use Spring Test + Mockito + JUnit 4 for Spring Boot + Spring Retry unit tests

Overview

--Automatically test sample program by Spring Boot + Spring Retry with Spring Test --Use Spring Test + Mockito + JUnit 4 for testing

Operation check environment

Source code list of sample program

├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── sample
    │           ├── EchoController.java
    │           ├── EchoRepository.java
    │           └── EchoService.java
    └── test
        └── java
            └── sample
                └── EchoServiceTest.java

Maven build file pom.xml

This time, run the build with Maven. pom.xml is based on the one generated by Spring Initializr. Add spring-retry to dependencies to use Spring Retry. Also, add spring-boot-starter-aop because AOP class is required at runtime. Add spring-boot-starter-test to use Spring Test.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.0.M4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>info.maigo.lab</groupId>
    <artifactId>sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sample</name>
    <description>Sample project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.retry/spring-retry -->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
            <version>1.2.4.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </pluginRepository>
    </pluginRepositories>

</project>

Source code

EchoController.java

It is a startup entry class by Spring Boot (with @SpringBootApplication annotation), and is also a Controller class (with @RestController annotation) that receives an HTTP request and returns a response. The @EnableRetry annotation is specified to enable Spring Retry.

package sample;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@SpringBootApplication
@EnableRetry
@RestController
public class EchoController {

    public static void main(String[] args) {
        SpringApplication.run(EchoController.class, args);
    }

    @Autowired
    private EchoService service;

    @RequestMapping("/{message}")
    public Map<String, Object> index(@PathVariable("message") String message) {
        return new HashMap<String, Object>() {
            {
                put("result", service.echo(message));
            }
        };
    }
}

EchoRepository.java

Repository class.

package sample;

import org.springframework.stereotype.Repository;

import java.util.Random;

@Repository
public class EchoRepository {

    public String echo(String message) {
        //Exception occurs with a half probability
        if (new Random().nextBoolean()) {
            return message;
        } else {
            throw new RuntimeException("Failure");
        }
    }
}

EchoService.java

Service class. Try the same method up to 3 times when RuntimeException occurs in the method with Spring Retry annotation @Retryable specified.

package sample;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

@Service
public class EchoService {

    @Autowired
    private EchoRepository repository;

    /**
     *Returns the specified message.
     *Occasionally it fails, but I try up to 3 times.
     * @param message message
     * @return message
     */
    @Retryable(
            value = {RuntimeException.class},
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000))
    public String echo(String message) {
        System.out.println("EchoService#echo: " + message);
        return repository.echo(message);
    }

    @Recover
    public String recover(RuntimeException e, String message) {
        System.out.println("EchoService#recover: " + message);
        throw e;
    }
}

EchoServiceTest.java

A test class for the EchoService class. Create a mock object of EchoRepository and test the behavior of EchoService.

package sample;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;

@RunWith(SpringRunner.class) //Use JUnit for Spring
@SpringBootTest //Start Spring Boot and test
public class EchoServiceTest {

    @Autowired
    private EchoService service;

    //Add a mock to the ApplicationContext
    // (Replace existing objects, if any)
    @MockBean
    private EchoRepository mockRepository;

    @Test
    public void testSuccess() {
        //Prepare a mock of EchoRepository
        //This mock returns a value normally
        String message = "Zero";
        doReturn(message).when(mockRepository).echo(message);
        //test
        assertEquals(message, service.echo(message));
    }

    @Test
    public void testFairureSuccess() {
        //Prepare a mock of EchoRepository
        //This mock throws an exception once and then returns the value normally
        String message = "Uno";
        doThrow(new RuntimeException("Failure"))
            .doReturn(message)
            .when(mockRepository).echo(message);
        //test
        assertEquals(message, service.echo(message));
    }

    @Test
    public void testFairureFairureSuccess() {
        //Prepare a mock of EchoRepository
        //This mock throws an exception twice and then returns a value normally
        String message = "Due";
        doThrow(new RuntimeException("Failure"))
            .doThrow(new RuntimeException("Failure"))
            .doReturn(message)
            .when(mockRepository).echo(message);
        //test
        assertEquals(message, service.echo(message));
    }

    @Test(expected = RuntimeException.class) //Assuming a RuntimeException
    public void testFairureFairureFairure() {
        //Prepare a mock of EchoRepository
        //This mock throws an exception 3 times
        String message = "Tre";
        doThrow(new RuntimeException("Failure"))
            .doThrow(new RuntimeException("Failure"))
            .doThrow(new RuntimeException("Failure"))
            .when(mockRepository).echo(message);
        //test
        service.echo(message);
    }
}

Run the test

Enter the mvn test command to launch Spring Boot and run the test.

$ mvn test
[INFO] Scanning for projects...
[INFO] 
[INFO] -----------------------< info.maigo.lab:sample >------------------------
[INFO] Building sample 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
(Omission)
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v2.2.0.M4)

(Omission)
2019-07-20 20:12:34.687  INFO 25426 --- [           main] sample.EchoServiceTest                   : Started EchoServiceTest in 3.407 seconds (JVM running for 4.983)
EchoService#echo: Due
EchoService#echo: Due
EchoService#echo: Due
EchoService#echo: Tre
EchoService#echo: Tre
EchoService#echo: Tre
EchoService#recover: Tre
EchoService#echo: Uno
EchoService#echo: Uno
EchoService#echo: Zero
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.643 s - in sample.EchoServiceTest
2019-07-20 20:12:39.930  INFO 25426 --- [       Thread-1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  14.650 s
[INFO] Finished at: 2019-07-20T20:12:40+09:00
[INFO] ------------------------------------------------------------------------

Reference material

Recommended Posts

Use Spring Test + Mockito + JUnit 4 for Spring Boot + Spring Retry unit tests
Use DBUnit for Spring Boot test
How to write a unit test for Spring Boot 2
Frequent annotations for Spring Boot tests
Unit test with Junit.
How to make Java unit tests (JUnit & Mockito & PowerMock)
[JUnit 5 compatible] Write a test using JUnit 5 with Spring boot 2.2, 2.3
Spring Boot + Springfox springfox-boot-starter 3.0.0 Use
Use Spring JDBC with Spring Boot
Spring Boot for annotation learning
Java unit tests with Mockito
Sample code to unit test a Spring Boot controller with MockMvc
[Spring Boot] Until @Autowired is run in the test class [JUnit5]
Use Basic Authentication with Spring Boot
How to unit test Spring AOP
Spring Boot for the first time
Write test code in Spring Boot
Read H2 database for unit tests
How to use ModelMapper (Spring boot)
Beginning with Spring Boot 0. Use Spring CLI
Test Spring framework controller with Junit
Workaround for Command Line Runner to work with JUnit in Spring Boot
How to not start Flyway when running unit tests in Spring Boot
[For internal use] For those assigned to the Spring Boot project (under construction)
JUnit unit test pre-processing and post-processing order
[Rails] Unit test code for User model
Java Artery-Easy to use unit test library
Perform transaction confirmation test with Spring Boot
Spring Boot @WebMvcTest test enables Spring Security default security
WebMvcConfigurer Memorandum of Understanding for Spring Boot 2.0 (Spring 5)
Use DynamoDB query method in Spring Boot
Use cache with EhCashe 2.x with Spring Boot
How to perform UT with Excel as test data with Spring Boot + JUnit5 + DBUnit