> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/architect/dan-yuan-ce-shi/springboot-dan-yuan-ce-shi-shi-jian.md).

# SpringBoot单元测试实践

```java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@Transactional
@Rollback(true)
public class HelloServiceTest {
 
    @Autowired
    private HelloService helloService;
 
    @Test
    public void sayHello() {
        helloService.sayHello("zhangsan");
        // XXX Asset
    }

```

1. `@SpringBootTest`启动了SpringBoot环境，`classes = Application.class`则扫描了该类下的所有的Bean
2. `@Transactional` 含有数据库操作，需要增加该注解
3. `@Rollback(true)` 数据库事务默认回滚，用来保障数据隔离

## 使用内存数据库 h2 + flayway为dao层测试

## mock feign client

Feign Client接口：

```java
@FeignClient(value = "baidu")
public interface BaiduApi {
    @GetMapping("index.html")
    String index();
}
```

上层业务逻辑：

```java
@Component
public class BaiduManager {
    @Resource
    private BaiduApi baiduApi;

    public String getIndex() {
        return baiduApi.index();
    }
}
```

单元测试：

```java
@SpringBootTest
class BaiduManagerTest {
    @Resource
    private BaiduManager baiduManager;

    // `@MockBean`会生成一个Mock对象，并取代已存在的相同类型的单例bean，如果不存在则添加
    @MockBean(name = "com.tenmao.utdemo.BaiduApi")
    private BaiduApi baiduApi;

    @Test
    void getIndex() {
        Mockito.when(baiduApi.index()).thenReturn("baidu index");

        String index = baiduManager.getIndex();
        Assertions.assertEquals("baidu index", index);
    }
}
```

注意：

* Mock对象并没有取代实际对象：`@MockBean(name = "com.tenmao.utdemo.BaiduApi")`需要设置name为全限定符

## @SpringBootTest 性能优化

1. `webEnvironment` 这个属性决定了测试类要不要启动一个 web 环境，说白了就是要不要启动一个 Tomcat 容器，可选的值为：
   * MOCK, 启动一个模拟的 Servlet 环境，这是默认值
   * RANDOM\_PORT，启动一个 Tomcat 容器，并监听一个随机的端口号
   * DEFINED\_PORT，启动一个 Tomcat 容器，并监听配置文件中定义的端口（未定义则默认监听8080）
   * NONE，不启动 Tomcat 容器。只需要通过指定 `@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)` 即可达到加速的效果。这时测试类启动时就只会初始化 Spring 上下文，不再启动 Tomcat 容器了!
2. `classes` 属性，用来指定运行测试类需要装载的 class 集合，如果不指定，那么会默认装载 `@SpringBootConfiguration` 注解标注的类。如果我们不指定classes属性，那么启动测试类时需要加载的Bean的数量和正常启动一次入口类(即有`@SpringBootApplication`注解的类)加载的 Bean 数量是一样的。
   * 特别是含有 `@PostConstruct` 注解指定的Bean以及实现了`CommandLineRunner`接口的Bean，他们可能会有很长的初始化时间。
   * 启动时，如果选择只加载需要的Bean上下文，就可以加快速度。
3. 使用`@ComponentScan`来指明Spring 扫描Bean的范围，也可以缩小Bean所扫描的范围
4. `@ContextConfiguration` 定义类级元数据，用于确定如何为集成测试加载和配置，简单的说就是通过该注解定义Spring容器在启动是需要加载的上下文（配置类、配置文件、实例化的Bean）,可以用于最小化测试，提升测试效率。
