</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
因为继承了`spring-boot-starter-parent`,所以我们依赖的`spring-boot-starter-test`不需要写具体的版本,可以直接集成父级的版本定义。其中,`spring-boot-starter-web`是用于提供 REST API 的 web 容器,`spring-boot-starter-test`可以提供各种测试框架的,`spring-boot-maven-plugin`是将 SpringBoot 应用打包为可执行 jar 的插件。
### 项目结构
因为是 DEMO 示例,我们实现一个 Echo 接口,能够接收请求参数,并返回加工后的字符串。按照惯例,我们使用万能的`Hello, World!`。
我们的项目结构如下:
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── cn
│ │ └── howardliu
│ │ └── effective
│ │ └── spring
│ │ └── springbootjunit5mockio
│ │ ├── SpringbootJunit5MockioApplication.java
│ │ ├── controller
│ │ │ └── EchoController.java
│ │ └── service
│ │ ├── EchoService.java
│ │ └── impl
│ │ └── EchoServiceImpl.java
│ └── resources
│ └── application.yaml
└── test
└── java
└── cn
└── howardliu
└── effective
└── spring
└── springbootjunit5mockio
└── controller
├── EchoControllerMockTest.java
└── EchoControllerNoMockitoTest.java
* SpringbootJunit5MockioApplication:SpringBoot 应用启动入口
* EchoController:接口定义
* EchoService:实现业务逻辑接口
* EchoServiceImpl:接口实现
* EchoControllerMockTest:使用 Mock 代理 EchoService 实现
* EchoControllerNoMockitoTest:直接测试接口实现
#### EchoServiceImpl
我们看下`EchoService`的实现,这将是我们 DEMO 的核心实现:
@Service
public class EchoServiceImpl implements EchoService {
@Override
public String echo(String foo) {
return "Hello, " + foo;
}
}
#### EchoControllerNoMockitoTest
我们先使用 Junit5+MockMvc 实现 Controller 接口的普通调用,代码如下:
@SpringBootTest(classes = SpringbootJunit5MockioApplication.class)
@AutoConfigureMockMvc
class EchoControllerNoMockitoTest {
@Autowired
private MockMvc mockMvc;
@Test
void echo() throws Exception {
final String result = mockMvc.perform(
MockMvcRequestBuilders.get("/echo/")
.param("name", "看山")
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn()
.getResponse()
.getContentAsString(StandardCharsets.UTF_8);
Assertions.assertEquals("Hello, 看山", result);
}
}
我们通过`SpringBootTest`注解定义这是一个 SpringBoot 应用的测试用例,然后通过`AutoConfigureMockMvc`启动测试容器。这样,就可以直接注入`MockMvc`实例测试 Controller 接口。
这里需要注意一点,网上很多教程会让写`@ExtendWith({SpringExtension.class})`这样一个注解,其实完全没有必要。通过源码我们可以知道,`SpringBootTest`注解已经添加了`ExtendWith`。
#### EchoControllerMockTest
这个测试用例中,我们通过 Mockito 组件代理`EchoService`的`echo`方法,代码如下:
@SpringBootTest(classes = SpringbootJunit5MockioApplication.class)
@ExtendWith(MockitoExtension.class)
@AutoConfigureMockMvc
class EchoControllerMockTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private EchoService echoService;
@BeforeEach
void setUp() {
Mockito.when(echoService.echo(Mockito.any()))
.thenReturn("看山说:" + System.currentTimeMillis());
}
@Test
void echo() throws Exception {
final String result = mockMvc.perform(
MockMvcRequestBuilders.get("/echo/")
.param("name", "看山的小屋")
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn()
.getResponse()
.getContentAsString(StandardCharsets.UTF_8);
Assertions.assertTrue(result.startsWith("看山"));
}
}
在这个示例中,我们需要注意`@ExtendWith(MockitoExtension.class)`注解,这个注解是用于引入`MockBean`的,我们通过对`echo`方法的拦截,使其返回我们定义好的响应结果。这种方式是为了在多系统或者多功能测试时,不需要真正调用接口。
比如,我们需要获取用户手机号,通常在接口中会校验用户有没有登录,我们就可以使用 Mockito 的能力代理登录验证,使结果永远是 true。