在Spring Boot + Spring Retry的单元测试中,使用Spring Test + Mockito + JUnit 4

简而言之

    • Spring Boot + Spring Retry によるサンプルプログラムを Spring Test で自動テストする

 

    テストには Spring Test + Mockito + JUnit 4 を使う

确认操作环境

    • macOS Mojave

 

    • OpenJDK 11.0.2

 

    • Spring Boot 2.2.0 M4

 

    Spring Retry 1.2.4

样例程序的源代码列表

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

Maven 用于构建的文件是 pom.xml。

这次我们将使用Maven进行构建。
pom.xml基于Spring Initializr生成的内容作为基础。
为了使用Spring Retry,我们需要在dependencies中添加spring-retry。另外,在运行时还需要AOP类,所以我们还需要添加spring-boot-starter-aop。
为了使用Spring Test,我们需要添加spring-boot-starter-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的中文意思为“回声控制器.java”。

这是一个由Spring Boot启动类(附带@SpringBootApplication注解)兼具Controller类(附带@RestController注解)的应用。为了启用Spring Retry,我们使用了@EnableRetry注解。

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 的中文翻译可能为”回声存储库.java”。

仓库类。

package sample;

import org.springframework.stereotype.Repository;

import java.util.Random;

@Repository
public class EchoRepository {

    public String echo(String message) {
        // 二分の一の確率で例外が発生する
        if (new Random().nextBoolean()) {
            return message;
        } else {
            throw new RuntimeException("Failure");
        }
    }
}

EchoService.java的中文同义表达:

回声服务.java

Service类。
在使用Spring Retry注解@Retryable指定的方法内发生RuntimeException的情况下,最多重试3次同一个方法。

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;

    /**
     * 指定したメッセージを返します。
     * たまに失敗しますが、3回まで試行します。
     * @param message メッセージ
     * @return メッセージ
     */
    @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的本地化中文译文:回声服务测试.java

以下是 EchoService 类的测试类。
我们需要生成 EchoRepository 的模拟对象来测试 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) // Spring 用の JUnit を使う
@SpringBootTest // Spring Boot を起動してテストする
public class EchoServiceTest {

    @Autowired
    private EchoService service;

    // ApplicationContext にモックを追加する
    // (既存のオブジェクトがあれば置き換える)
    @MockBean
    private EchoRepository mockRepository;

    @Test
    public void testSuccess() {
        // EchoRepository のモックを用意する
        // このモックは正常に値を返す
        String message = "Zero";
        doReturn(message).when(mockRepository).echo(message);
        // テスト
        assertEquals(message, service.echo(message));
    }

    @Test
    public void testFairureSuccess() {
        // EchoRepository のモックを用意する
        // このモックは例外を1回投げたあと、正常に値を返す
        String message = "Uno";
        doThrow(new RuntimeException("Failure"))
            .doReturn(message)
            .when(mockRepository).echo(message);
        // テスト
        assertEquals(message, service.echo(message));
    }

    @Test
    public void testFairureFairureSuccess() {
        // EchoRepository のモックを用意する
        // このモックは例外を2回投げたあと、正常に値を返す
        String message = "Due";
        doThrow(new RuntimeException("Failure"))
            .doThrow(new RuntimeException("Failure"))
            .doReturn(message)
            .when(mockRepository).echo(message);
        // テスト
        assertEquals(message, service.echo(message));
    }

    @Test(expected = RuntimeException.class) // RuntimeException が発生することを想定
    public void testFairureFairureFairure() {
        // EchoRepository のモックを用意する
        // このモックは例外を3回投げる
        String message = "Tre";
        doThrow(new RuntimeException("Failure"))
            .doThrow(new RuntimeException("Failure"))
            .doThrow(new RuntimeException("Failure"))
            .when(mockRepository).echo(message);
        // テスト
        service.echo(message);
    }
}

进行考试

输入mvn test命令,Spring Boot将启动并执行测试。

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

(中略)
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] ------------------------------------------------------------------------

范例资料

    • GitHub – spring-projects/spring-retry

 

    • Testing improvements in Spring Boot 1.4

 

    • Spring Boot Reference Documentation – 4.25. Testing

 

    • SpringBootTest (Spring Boot Docs 2.2.0.M4 API)

 

    • SpringRunner (Spring Framework 5.2.0.M3 API)

 

    • org.junit (JUnit API)

 

    • MockBean (Spring Boot Docs 2.2.0.M4 API)

 

    • Spring Retry を利用して宣言型のリトライ処理を実装する – Qiita

 

    • @MockBeanと@Mock、Mockito.mock()の違い | 技術系のメモ

 

    • 心地良すぎるモックライブラリ Mockito 〜その2〜 – A Memorandum

 

    • Mockito~doReturn/whenとwhen/thenReturnの違い | GWT Center

 

    Spring BootでAutowiredされるクラスをMockitoでモックする – Qiita
广告
将在 10 秒后关闭
bannerAds