掘金 后端 ( ) • 2024-04-19 16:34

前言

在开发中,为了验证代码的可行性,会写测试单元去验证,执行单元测试,就是为了证明这 段代码的行为和我们期望是否一致

接口单元测试

1、Springboot2.4版本以上移除了junit4.x版本依赖,如果需要引入junit4.x版本的,请自行引入,maven仓库地址为:  https://mvnrepository.com/artifact/junit/junit/4.13.2

2、Springboot高版本默认使用了junit5单元测试

junit5注解说明

注解 说明 @Test 表示方法是测试方法,但是与 JUnit4 的 @Test 不同,它的职责非常单一不能声明任何属性,拓展的测试将会由 Jupiter 提供额外测 @DisplayName 为测试类或者测试方法设置展示名称 @BeforeAll 表示在所有单元测试之前执行(被修饰的方法不必须是静态方法) @BeforeEach 表示在每个单元测试之前执行 @Timeout 表示测试方法运行如果超过了指定时间将会返回错误 @Disabled 表示测试类或测试方法不执行 @RepeatedTest 表示方法可重复执行 @ExtendWith 为测试类或测试方法提供扩展类引用 @AfterEach 表示在每个单元测试之后执行 @AfterAll 表示在所有单元测试之后执行(被修饰的方法必须是静态方法)

单元测试编写

引入以下依赖

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

MockMvc使用

MockMvc是由spring-test包提供,实现了对Http请求的模拟,其实例化为

       private MockMvc mockMvc;

	@Autowired
	private WebApplicationContext wac;

	@BeforeEach
	void setup() {
		this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
	}

测试单元编写

(1)先写一个接口

public class UserQuery {

    
    private Long id;


    private String account;


    private String username;

    private Long deptId;


    private List<Long> roleIds;
    
    private String password;
    

}
  @PostMapping
    public R<Boolean> save(@RequestBody UserQuery userQuery) {
        log.info("保存数据为:{{}}", userQuery);
        return R.ok(null, "操作成功");
    }

再用测试单元对该接口进行测试

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import cn.com.ut.utsmart.common.core.util.R;
import cn.com.ut.utsmart.light.query.user.UserQuery;
import cn.com.ut.utsmart.light.service.client.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
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.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@SpringBootTest
@DisplayName("UserController测试")
public class LightUserControllerTest {

	@MockBean
	UserService userService;

	@MockBean
	private RedisTemplate<String, String> redisTemplate;

	// @Autowired
	private MockMvc mockMvc;

	@Autowired
	private ObjectMapper objectMapper;

	@Autowired
	private WebApplicationContext wac;

	@BeforeEach
	void setup() {
		this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
	}

	@Nested
	@DisplayName("用户保存单元测试")

	public class UserAddTest {

		@Test
		@DisplayName("请求头合法,Method(POST),ContentType(application/json)) => HTTP_STATUS:200")
		public void save_1_test() throws Exception {
			UserQuery userQuery = new UserQuery();
			userQuery.setUsername("aa");
			userQuery.setDeptId(1L);
			userQuery.setAccount("aa");
			userQuery.setRoleIds(Collections.singletonList(1L));

			mockMvc
				.perform(post("/light/user").content(objectMapper.writeValueAsString(userQuery))
					.contentType("application/json"))
				.andExpect(status().isOk());
		}

		@Test
		@DisplayName("请求不合法,Method(Get) => HTTP_STATUS:405")
		public void save_2_test() throws Exception {
			UserQuery userQuery = new UserQuery();
			userQuery.setUsername("aa");
			userQuery.setDeptId(1L);
			userQuery.setAccount("aa");
			userQuery.setRoleIds(Collections.singletonList(1L));

			mockMvc
				.perform(get("/light/user").content(objectMapper.writeValueAsString(userQuery))
					.contentType("application/json"))
				.andExpect(status().is(HttpStatus.METHOD_NOT_ALLOWED.value()));
		}

		@Test
		@DisplayName("请求内容不合法,调用service.save()失败 => 返回失败")
		public void save_3_test() throws Exception {
			UserQuery userQuery = new UserQuery();
			userQuery.setUsername("aa");
			userQuery.setAccount("aa");
			userQuery.setRoleIds(Collections.singletonList(1L));
			when(userService.save(userQuery)).thenReturn(false);

			// when
			MvcResult mvcResult = mockMvc
				.perform(post("/light/user").content(objectMapper.writeValueAsString(userQuery))
					.contentType("application/json"))
				.andExpect(status().isOk())
				.andReturn();

			// then
			String actualRespBody = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
			String expectRespBody = objectMapper.writeValueAsString(R.ok(false, "操作成功"));
			assertThat(actualRespBody).isEqualToIgnoringCase(expectRespBody);
			verify(userService, times(1)).save(Mockito.any(UserQuery.class));
		}

		@Test
		@DisplayName("请求内容合法,调用service.save()成功 => 返回成功")
		public void save_4_test() throws Exception {
			UserQuery userQuery = new UserQuery();
			userQuery.setUsername("aa");
			userQuery.setAccount("aa");
			userQuery.setRoleIds(Collections.singletonList(1L));
			when(userService.save(userQuery)).thenReturn(true);
			// when
			MvcResult mvcResult = mockMvc
				.perform(post("/light/user").content(objectMapper.writeValueAsString(userQuery))
					.contentType("application/json"))
				.andExpect(status().isOk())
				.andReturn();
			// then
			String actualRespBody = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
			String expectRespBody = objectMapper.writeValueAsString(R.ok(true, "操作成功"));
			assertThat(actualRespBody).isEqualToIgnoringCase(expectRespBody);

			verify(userService, times(1)).save(userQuery);
		}

		@Test
		@DisplayName("参数认证不合法,账号为空,Method(POST),ContentType(application/json)) => HTTP_STATUS:400")
		public void save_5_test() throws Exception {
			UserQuery userQuery = new UserQuery();
			userQuery.setUsername("aa");
			userQuery.setDeptId(1L);
			userQuery.setRoleIds(Collections.singletonList(1L));

			MvcResult mvcResult = mockMvc
				.perform(post("/light/user").content(objectMapper.writeValueAsString(userQuery))
					.contentType("application/json"))
				.andExpect(status().is(HttpStatus.BAD_REQUEST.value()))
				.andReturn();
			String actualRespBody = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
			String expectRespBody = objectMapper.writeValueAsString(R.failed("账号不能为空"));
			assertThat(actualRespBody).isEqualToIgnoringCase(expectRespBody);
		}

	}

}

assertThat使用

assertThat是junit4.4的一个新断言语句,结合 Hamcrest 提供的匹配符,可以用来匹配值是否与期望一致

@SpringBootTest
@DisplayName("测试")
public class HelloTest {

	@Test
	public void hello() {
		String data = "aaa";
		assertThat(data).isEqualTo("aaa");
	}

}

总结

在写方法或者接口过程中,我们可以根据测试用例,写对应的单元测试去测试接口或者方法是否符合预期