赞
踩
JUnit:
JUnit 5是JUnit的最新版本
,它引入了全新的编程模型和扩展模型,使得编写和扩展测试更加灵活和强大。TestNG:
Mockito:
通常与JUnit一起使用
。它提供了一个简单且灵活的API来创建和配置模拟对象。PowerMock:
扩展了EasyMock和Mockito的功能
,支持对静态方法、构造函数、私有方法等进行模拟。AssertJ:
Java断言库
,它提供了更自然和富有表达力的方式来编写断言代码。与JUnit等测试框架结合使用,可以使测试代码更加清晰和易于理解。Hamcrest:
匹配器库
,它提供了丰富的匹配器来构建复杂的断言条件
。Hamcrest与JUnit等测试框架配合使用
,可以使断言更加灵活和强大。学习单元测试和断言前请先了解 【Java基础】使用Junit5进行单元测试 基础
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
spring-boot-starter-test中包含了junit和mockito
等依赖
相关依赖
丰富的匹配器来构建复杂的断言条件
。Hamcrest与JUnit等测试框架配合使用
,可以使断言更加灵活和强大。@DisplayName("TestDemo测试类") //起别名
@SpringBootTest //1.类上添加注解,加载ApplicationContext,启动spring容器。
@AutoConfigureMockMvc //2.启动mockMVC测试
@Transactional //3.开启事务管理
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)//开启测试类的执行顺序按@order配置的优先级执行
public class TestDemo {
@Test
@Order(3)//第3个执行
public void test1() {
int a = 1;
Assertions.assertNotEquals(1, a);//判断二者是否不相等
}
@Test
@Order(2)//第2个执行
public void test2() {
int a = 1;
Assertions.assertNotEquals(1, a);//判断二者是否不相等
}
@Test
@Order(1)//第一个执行
public void test3() {
int a = 1;
Assertions.assertNotEquals(1, a);//判断二者是否不相等
}
}
执行结果
一 般情况下,使用@SpringBootTest
后,Spring将加载所有被管理的bean
,基本等同于启动了整个springboot服务
,此时便可以开始功能测试。
可以通过webEnvironment参数启动的Web环境对应的端口,springboot提供了4种设置如下:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
默认值
,该类型提供一个mock环境,可以和@AutoConfigureMockMvc
或@AutoConfigureWebTestClient
搭配使用,开启Mock相关的功能。注意此时内嵌的服务(servlet容器)
并没有真正启动,也不会监听web服务端口
。建议
)application.properties
读取)。@Mock :是Mockito.mock()
方法的简写。创建的是全部mock的对象,即在对具体的方法打桩(即创建模拟对象
)之前,mock对象的所有属性和方法全被置空(0或null)
。
MockitoAnnotations.openMocks(testClass)
方法一起使用.@Spy
:是Mockito.Spy()
方法的简写。被 spy 的对象,调用其方法时默认会走真实方法。
,有返回值的调用真实方法并返回真实值
虚假函数
);默认生成后所有依赖的对象都会null,且要一个无参构造
。@InjectMocks :将 @Mock、@Spy 修饰的对象自动注入到 @InjectMocks 修饰的对象中。
注入方式有多种,mockito 会按照下面的顺序尝试注入:
//类1
public class HttpService {
public int queryStatus() {
// 发起网络请求,提取返回结果
// 这里用随机数模拟结果
return new Random().nextInt(2);
}
}
//类2
public class ExampleService {
private HttpService httpService;
public String hello() {
int status = httpService.queryStatus();
if (status == 0) {
return "你好";
}
else if (status == 1) {
return "Hello";
}
else {
return "未知状态";
}
}
}
public class ExampleServiceTest {
@InjectMocks // 将@Mock httpService主动注入ExampleService
private ExampleService exampleService = new ExampleService();
@Mock
private HttpService httpService;
@Test
public void test01() {
MockitoAnnotations.initMocks(this);
when(httpService.queryStatus()).thenReturn(0);
Assert.assertEquals("你好", exampleService.hello());
}
}
@MockBean : Spring Boot 中的注解。我们可以使用 @MockBean
将 mock 对象添加到 Spring 应用程序上下文中
。该 mock 对象将替换应用程序上下文中任何现有的相同类型的 bean。如果应用程序上下文中没有相同类型的 bean,它将使用 mock 的对象作为 bean 添加到上下文中。
@SpyBean:同上。
会真实操作数据库
,如果在单元测试中不想改变数据数据库中的值,不能使用直接注入的方法可以在类上再添加这两个注解,通过
@Transactional+@Rollback(true)
可以知道调用了数据库,对其操作进行回滚
但是如果项目中使用了@Component注解(在SpringBoot项目启动的时候就会跟着实例化/启动),@Component注解的类里有多线程方法,那么在执行单元测试的时候,由于多线程任务的影响,就可能对数据库造成了数据修改,
- 即使使用了事务回滚注解@Transactional。(我在百度上看到的,没找到具体的测试方法,所以没试)
@Transactional
@Rollback(true) // 事务自动回滚,默认是true。可以不写
所谓的mock就是创建一个类的虚拟对象
,在测试环境中,用来替换掉真实的对象
,以达到2个目的:
使用Mock之前,需要在@Before或@BeforeClass
对应的方法中添加如下,表示 添加mock注解初始化。
MockitoAnnotations.initMocks(this);
另外需要补充以下几个常用的测试注解:
@InjectMocks:通过创建一个实例,它可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
@Mock:该对象下对函数的调用均执行mock(即虚假函数)
,不执行真正具体操作。
@Spy:对函数的调用均执行真正部分。
Mockito中
的@Mock和@Spy都可用于拦截那些尚未实现
或 不期望被真实调用的对象和方法
,并为其设置自定义行
为。
于Mock不真实调用,Spy会真实调用
。Mockito 默认是“不支持静态方法,可使用 PowerMock 让 Mockito 支持静态方法(新增依赖)
实现原理:使用Stub(桩)技术动态
的替换原程序的功能。
模拟实际对象或方法
的调用。使用Mock的优点:
Mockito.mock(xxx.class) 创建mock对象
Mockito.mock(classToMock,defaultAnswer) 使用默认Answer模拟对象
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.Random;
public class MockitoFirstDemo {
@Test
public void test() {
//mock了一个Random对象
Random mockRandom = Mockito.mock(Random.class);
System.out.println("mock前:"+mockRandom.nextInt());
Assert.assertEquals(0, mockRandom.nextInt());//未进行打桩,每次返回值都是0
//设置random.nextInt()虚拟值为100
Mockito.when(mockRandom.nextInt()).thenReturn(100); // 进行打桩操作,指定调用 nextInt 方法时,永远返回 100
System.out.println("mock后:"+mockRandom.nextInt());
Assert.assertEquals(100, mockRandom.nextInt());
}
}
·
Mockito.doThrow(toBeThrown).when(mock).[method] 模拟抛出异常
//如果mockRandom对象调用nextInt()方法 抛出空指针异常
Mockito.doThrow(new NullPointerException()).when(mockRandom).nextInt();
mockRandom.nextInt();
Mockito.when(methodCall).thenReturn(value) 模拟方法调用返回值
Mockito.doReturn(toBeReturned).when(mock).[method] 模拟方法调用返回值(直接执行不判断)
Mockito.when(methodCall).thenReturn(value1).thenReturn(value2) 模拟多次方法调用返回值,触发时第一次返回value1,第n次都返回value2
//1.模拟nextInt方法调用返回100
Mockito.when(mockRandom.nextInt()).thenReturn(100);
//2.触发时方法调用nextInt第一次返回101,nextInt的第n次都返回102(可以一直设置值)
Mockito.when(mockRandom.nextInt()).thenReturn(101).thenReturn(102);
//3.模拟nextInt方法调用返回100 (同第一种)
Mockito.doReturn(100).when(mockRandom).nextInt();
System.out.println(mockRandom.nextInt());//返回100
Mockito.when(methodCall).thenAnswer(answer)) 自定义模拟方法的返回值,可以根据方法的传参定义方法的返回
Mockito.doAnswer(answer).when(methodCall).[method] 自定义模拟方法的返回值,可以根据方法的传参定义方法的返回
@Test
public void test() {
// mock一个对象
HashMap mockMap = Mockito.mock(HashMap.class);
mockMap.put("key1", "value1");
mockMap.put("key2", "value2");
Mockito.when(mockMap.get(ArgumentMatchers.anyString())).thenAnswer(
new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
String key = (String) args[0];
//修改key=key1的返回值
if (key.equals("key1")) {
return "called with arguments: " + Arrays.toString(args);
}
//修改key=key2的返回值
if (key.equals("key2")) {
return "called with arguments: " + Arrays.toString(args);
}
return "error key";
}
});
System.out.println(mockMap.get("key1")); // called with arguments: [key1]
System.out.println(mockMap.get("key2")); // called with arguments: [key2]
System.out.println(mockMap.get("key3")); //error key
}
Mockito.verify(mock) 验证对象的方法调用是否发生
//创建mock对象
ArrayList list = Mockito.mock(ArrayList.class);
list.add(1);
list.add(2);
Mockito.verify(list).add(1);//验证通过
Mockito.verify(list).add(5);//验证未通过,因为没有执行过该操作
Mockito.spy(Object) 用spy监控真实对象,设置真实对象行为
//虚假调用
ExampleService mockExample = Mockito.mock(ExampleService.class);
int num = mockExample.add(1, 1);
System.out.println("虚假调用>>>"+num);
//返回虚假调用>>>0
ExampleService spyExample = Mockito.spy(ExampleService.class);
num = spyExample.add(2, 2);
System.out.println("真实调用>>>"+num);
//真实调用方法,参数a=2,参数b=2
//真实调用>>>4
when().Return() 与 doReturn() 设置方法的返回值
Mockito.when(mock.someMethod("some args")).Return("result");
Mockito.doReturn("result").when(mock).someMethod("some arg");
when().thenthrow() 与 doThrow() 让方法抛出异常
// 只针对返回值非void的函数
Mockito.when(mock.someMethod("some args")).thenthrow(new Exception("自定义异常"));
// 通用
Mockito.doThrow(new Exception("自定义异常"))
.when(mock)
.someMethod("some arg");
doNothing() 让void函数什么都不做
Mockito.doNothing().when(mock).someMethod("some args");
doAnswer()自定义方法处理逻辑
// 自定义返回值thenAnswer()
when(mock.someMethod(anyString())).thenAnswer(
new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return "called with arguments: " + Arrays.toString(args);
}
});
//Following prints "called with arguments: [foo]"
System.out.println(mock.someMethod("foo"));
thenCallRealMethod()调用 spy 对象的真实方法
Mockito.when(spy.someMethod("some args")).thenCallRealMethod();
Mockito.doCallRealMethod().when(spy).someMethod("some arg");
使用then、thenAnswer 自定义方法处理逻辑
// 自定义返回值thenAnswer()
when(mock.someMethod(anyString())).thenAnswer(
new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return "called with arguments: " + Arrays.toString(args);
}
});
//Following prints "called with arguments: [foo]"
System.out.println(mock.someMethod("foo"));
@Test
void testDemo02() {
Mockito.when(studentService.getStudentByUserName("张三")).thenAnswer(
(Answer<Student>) invocationOnMock -> new Student("赵六","13215522144","河南省")
);
Student student = studentService.getStudentByUserName("张三");
// prints: Student{username='赵六', phone='13215522144', address='河南省'}
System.out.println(student.toString());
}
reset()方法,可以重置之前自定义的返回值和异常
import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.*;
public class MockitoDemo {
static class ExampleService {
public int add(int a, int b) {
return a+b;
}
}
@Test
public void test() {
ExampleService exampleService = mock(ExampleService.class);
// mock 对象方法的默认返回值是返回类型的默认值
Assert.assertEquals(0, exampleService.add(1, 2));
// 设置让 add(1,2) 返回 100
when(exampleService.add(1, 2)).thenReturn(100);
Assert.assertEquals(100, exampleService.add(1, 2));
// 重置 mock 对象,add(1,2) 返回 0
reset(exampleService);
Assert.assertEquals(0, exampleService.add(1, 2));
}
Mockito.mock()
的一个替代要使用MockitoAnnotations.initMocks 方法,让注解生效
。
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Random;
import static org.mockito.Mockito.when;
public class MockitoTwoDemo {
@Mock
private Random random;
@Before
public void before() {
// 初始化mock,让注解生效(新版)
MockitoAnnotations.openMocks(this);
}
@Test
public void test() {
//设置random.nextInt()虚拟值为100
when(random.nextInt()).thenReturn(100);
System.out.println("mock后:"+random.nextInt());
Assert.assertEquals(100, random.nextInt());
}
}
也可以用MockitoJUnitRunner来代替MockitoAnnotations.openMocks
mock()方法与spy()方法的不同:
对象实例
,mock的参数是class
@InjectMocks由mock框架管理,只能将 @Mock、@Spy
修饰的对象自动注入到@InjectMocks
修饰的对象中
@Mock
AService aService;
@InjectMocks
AController aController; //这里会注aService
@Autowired
AController aController;//这里不会注aService
class BController{
AService aService;
}
如果想一个spring对象注入mock框架的对象,可通过@InjectMocks桥接。
@Mock
AService aService;
@Autowired
@InjectMocks
AController aController;//这里会注入aService
@MockBean和@SpyBean
由spring管理,会替换上下文相同对象。
@MockBean
AService aService;
@Autowired
AController aController; //这里会注入aService
thenReturn 用来指定特定函数和参数调用的返回值
;
指定多个返回值
。在调用时返回值依次返回。 若调用次数超过返回值的数量
,再次调用时返回最后一个返回值
。doReturn 的作用和 thenReturn 相同,但使用方式不同:
//mockRandom.nextInt()返回虚拟值1
Mockito.when(mockRandom.nextInt()).thenReturn(1);//返回值为1
//mockRandom.nextInt()依次返回虚拟值1 2 3
Mockito.when(mockRandom.nextInt()).thenReturn(1, 2, 3);
//mockRandom.nextInt()的返回值设置为1
Mockito.doReturn(1).when(random).nextInt();
thenThrow 用来让函数调用抛出异常。(可搭配try catch使用)
可以指定多个异常
。在调用时异常依次返回
。若调用次数超过异常的数量,再次调用时抛出最后一个异常。//调用mockRandom.nextInt()抛出RuntimeException异常
Mockito.when(mockRandom.nextInt()).thenThrow(new RuntimeException("异常"));
//调用mockRandom.nextInt()依次抛出RuntimeException异常
Mockito.when(mockRandom.nextInt()).thenThrow(new RuntimeException("异常1"), new RuntimeException("异常2"));
@Test
public void testThenThrow() {
Random mockRandom = mock(Random.class);
//调用mockRandom.nextInt()抛出RuntimeException异常
Mockito.when(mockRandom.nextInt()).thenThrow(new RuntimeException("异常"));
try {
mockRandom.nextInt();
Assert.fail();//上一行会抛出异常,到catch中去,走不到这里
} catch (Exception ex) {
Assert.assertTrue(ex instanceof RuntimeException);
Assert.assertEquals("异常1", ex.getMessage());
}
try {
mockRandom.nextInt();
Assert.fail();
} catch (Exception ex) {
Assert.assertTrue(ex instanceof RuntimeException);
Assert.assertEquals("异常2", ex.getMessage());
}
}
void 的函数
,thenThrow 是无效的,要使用 doThrow
。也可以用 doThrow 让返回非void的函数抛出异常
doThrow(new RuntimeException("异常")).when(exampleService).hello();
// 下面这句等同于 when(random.nextInt()).thenThrow(new RuntimeException("异常"));
doThrow(new RuntimeException("异常")).when(random).nextInt();
//是否调用过一次
Mockito.verify(spy).hasReturnAndArgs(Mockito.anyString());
//是否调用过N次
Mockito.verify(spy,times(1)).hasReturnAndArgs(Mockito.anyString());
//没有被调用,相当于 times(0)
Mockito.verify(spy,never()).hasReturnAndArgs(Mockito.anyString());
//atLeast(N) 至少被调用 N 次
//atLeastOnce() 相当于 atLeast(1)
//atMost(N) 最多被调用 N 次
spring环境使用@MockBean+@SpyBean+@Autowired,为测试主体类部分打桩考虑使用@SpyBean
, 为外部依赖打桩
,考虑使用@MockBean
//业务层
@Service
public class AService {
public String hasReturnAndArgs(String str){
return "10";
}
public String hasReturn(){
return "10";
}
public void hasArgs(String str){
System.out.println(1000);
}
public void noArgs(){
System.out.println(1000);
}
}
//控制层
@RestController
public class AController {
@Autowired //注入aService
private AService aService;
public String hasReturnAndArgs(String str){
return aService.hasReturnAndArgs(str);
}
public String hasReturn(){
return aService.hasReturn();
}
public void hasArgs(String str){
aService.hasArgs(str);
}
public void noArgs(){
aService.noArgs();
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ServiceTest {
@Before
public void before() {
// 启用 Mockito 注解
MockitoAnnotations.initMocks(this);
}
@Mock //mock AService
AService aService;
@InjectMocks //将 @Mock、@Spy 修饰的对象自动注入到@InjectMocks修饰的对象中
AController aController;
@Test
public void test() {
//1.不调用真实方法,默认返回null
String value = aService.hasReturnAndArgs("10");
Assert.assertEquals(value, null);
//2.打桩
//当传参是10L时,返回 30
Mockito.when(aService.hasReturnAndArgs("10")).thenReturn("30");
//当传参是20L时,真实调用
Mockito.when(aService.hasReturnAndArgs("20")).thenCallRealMethod();
//当传参是30L时,抛出异常
Mockito.when(aService.hasReturnAndArgs("30")).thenThrow(new Exception("test error"));
//断言方法传参为10时是否等于 30,
Assert.assertEquals(aService.hasReturnAndArgs("10"), "30");
//当传参是20L时,真实调用方法,内部mock对象调用的也是mock方法
Assert.assertNotEquals(aService.hasReturnAndArgs("20"), "30");
try {
Assert.assertNotEquals(aService.hasReturnAndArgs("30"), "30");
} catch (Exception e) {
System.out.println(e.getMessage());
}
//3.注入对象
Assert.assertEquals(aController.hasReturnAndArgs("10"), "30");
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ServiceTest {
@Before
public void before() {
// 启用 Mockito 注解
MockitoAnnotations.initMocks(this);
}
@Spy
AService spy;
@Test
public void test() {
//AService spyTemp = new AService();
//AService spy = Mockito.spy(spyTemp);
//1.调用真实方法
Assert.assertEquals(spy.hasReturnAndArgs("20"), "10");
//2.打桩
Mockito.doReturn("30").when(spy).hasReturnAndArgs("20");
Assert.assertEquals(spy.hasReturnAndArgs("20"), "30");
//验证是否被调用了一次
Mockito.verify(spy,times(1)).hasReturnAndArgs("20");
//设置任何hasReturnAndArgs调用都返回30
Mockito.doReturn("30").when(spy).hasReturnAndArgs(Mockito.anyString());
Assert.assertEquals( spy.hasReturnAndArgs("-2"), "30");
Mockito.verify(spy,times(2)).hasReturnAndArgs(Mockito.anyString());
//不支持这样
Mockito.when(spy.hasReturnAndArgs("20")).thenReturn("10");
Assert.assertEquals(spy.hasReturnAndArgs("20"), "10");
}
}
使用spring集成
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ServiceTest {
@Before
public void before() {
// 启用 Mockito 注解
MockitoAnnotations.initMocks(this);
}
@SpyBean
private AService spy;
@Autowired
AController aController;
@Test
public void test() {
//调用真实方法
Assert.assertEquals(spy.hasReturnAndArgs("20"), "10");
Mockito.doReturn("30").when(spy).hasReturnAndArgs(Mockito.anyString());
Assert.assertEquals(spy.hasReturnAndArgs("20"), "30");
Mockito.verify(spy,times(1)).hasReturnAndArgs(Mockito.anyString());
Assert.assertEquals(aController.hasReturnAndArgs("20"), "30");
}
}
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.0.0</version>
</dependency>
MockitoAnnotations.initMocks(this);
Mockito.mockStatic(XXX.class).when(XXX::getXXX)
.thenReturn("xxx");
//如果用多次需要关闭
try(MockedStatic<XXX> xx= Mockito.mockStatic(XXX.class)) {
xx.when(() -> A.b(params)).thenReturn(null);
}
红色为尚未覆盖的行,绿色为覆盖的行。class,method,line分别表示类/方法/行代码测试覆盖率
假如我springboot项目有一个application.yml文件
test:
prop: testValue1
当编写单元测试测试的时候,在不修改源码的情况下,想改变prop属性为testValue2,该怎么办呢?
加载测试临时属性
可以通过注解@SpringBootTest的properties
和args属性
进行设定,作用域仅限于当前测试用例@Slf4j
@SpringBootTest(properties = {"test.prop=testValue2"})
class PropertiesAndArgsTest {
@Value("${test.prop}")
private String msg;
@Test
void test01() {
log.info(msg);
}
}
springMVC框架的测试中,一般采用mockMvc+Mockito
的组合来进行mock模拟测试,即:Mockito模拟服务层的方法, MockMvc 来模拟发起HTTP请求
在单元测试中对controller层功能进行测试,必须模拟一个真实的web环境,具体步骤如下:
测试类中启动web环境
每一个springboot的测试类都需@SpringBootTest
注解,通过webEnvironment属性设置在测试用例中启动web环境,具体如下:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WebTest {
}
测试类中发送请求
@RestController
@RequestMapping("/user")
@Slf4j
public class TestController {
/**
* post请求
* @param param json数据
* @return json数据
*/
@PostMapping("/post")
public Map<String, Object> post(@RequestBody Map<String, Object> param) {
log.info(">>>>>>>>>post user:{}", param);
Map<String, Object> data = new HashMap<>();
data.put("id", 2);
data.put("username", "post");
return data;
}
/**
* get请求,接收json以及 地址栏参数
* @param param json数据
* @param id 地址栏参数
* @return json数据
*/
@GetMapping("/get")
public Map<String, Object> get(@RequestBody Map<String, Object> param,@RequestParam("id") Integer id) {
log.info(">>>>>>>>>get user:{},id={}", param,id);
Map<String, Object> data = new HashMap<>();
data.put("id", 1);
data.put("username", "get");
return data;
}
}
在测试类中通过@AutoConfigureMockMvc
开启web虚拟调用功能
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
//1.测试类中启动web环境
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//日志调用
@Slf4j
//2.开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {
//3.注入MockMVC
@Autowired
MockMvc mockMvc;
/**
* 测试post请求
*
* @throws Exception
*/
@Test
void testUserPost() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/user/post")
//g请求参数为json
.content("{\"username\":\"oyang\",\"password\":\"123456\"}")
.header("Authorization", "Bearer ...")
.contentType(MediaType.APPLICATION_JSON)
)
//预期响应状态为200
.andExpect(MockMvcResultMatchers.status().isOk())
// 可以取出 json的字段值,判断code是否为0 响应结果:
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("0"))
.andReturn();
//{"code":0,"msg":"success","time":"20240327150240","data":{"id":2,"username":"post"},"requestId":null}
log.info(">>>>>mock响应结果:{}", mvcResult.getResponse().getContentAsString());
}
/**
* 测试get请求
*
* @throws Exception
*/
@Test
void testUserGet() throws Exception {
int id = 111;
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/user/get")
//get请求参数在url上
.param("id", "" + id)
//get请求参数为json
.content("{\"username\":\"oyang\",\"password\":\"123456\"}")
.header("Authorization", "Bearer ...")
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn()
; //预期响应状态为200
//{"code":0,"msg":"success","time":"20240327150240","data":{"id":1,"username":"get"},"requestId":null}
log.info(">>>>>mock响应结果:{}", mvcResult.getResponse().getContentAsString());
}
对待类中私有方法
,可以用反射的方式进行测试
spring框架中使用封装的反射API,来设置private的属性:
ReflectionTestUtils.setField(Object targetObject, String name, @Nullable Object value);
//或者
Field field = ReflectionUtils.findField(targetClass, name, type);
if (field == null) {
}
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, targetObject, value);
如果是非spring框架,也可以直接使用Java原生反射API:
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true); //改成可访问,不管现有修饰
field.set(target, value);
maven打包时使用命令打包时跳过test
mvn deploy -f pom_http.xml-jar -Dmaven.test.skip=true
Mockito 默认是不支持静态方法
,可使用 PowerMock 让 Mockito 支持静态方法(新增依赖)
服务层、存储库、REST客户端等组
件,而无需依赖实际的实现。来减少测试对外部系统的依赖,模拟异常情况和边缘用例,从而确保代码在各种环境下的稳健性。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。