--Automatically test sample program by Spring Boot + Spring Retry with Spring Test --Use Spring Test + Mockito + JUnit 4 for testing
├── pom.xml
└── src
├── main
│ └── java
│ └── sample
│ ├── EchoController.java
│ ├── EchoRepository.java
│ └── EchoService.java
└── test
└── java
└── sample
└── EchoServiceTest.java
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>
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);
}
}
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] ------------------------------------------------------------------------
Recommended Posts