Spring单元测试中@SpyBean和@MockBean区别
1、默认行为不同
对于没有用Mockito代理的方法,@SpyBean会调用真实的方法,有返回值的会返回方法的真实返回值(参考下方SpyBeanTest的testException4)。
@MockBean不管有没有用Mockito代理方法,都不会调用真实的方法,有返回值的会返回null(参考下方MockBeanTest的testException4)。
2、Mockito代理的方式不同
@MockBean的代理使用方式:Mockito.when(methodService.testException(anyBoolean())).thenReturn("1","2"...);
@SpyBean的代理使用方式:Mockito.doReturn("1","2"...).when(methodService).testException(anyBoolean());
注:
-
return里的参数:其中"1"表示第一次调用testException的返回值,"2"表示第二次调用testException的返回值,依次类推,只有一个时,每次调用都会返回相同的值。
-
anyBoolean()表示输入参数只要是boolean类型,即会返回指定的模拟值。如果代理方法时写为testException(true),则只有输入参数为true时,才会返回指定的模拟值。
3、@SpyBean和@MockBean应用
(1)@SpyBean使用代理控制返回值
(1)Mockito.doReturn("").when(methodService).testException(false);
不会执行方法体的内容,直接返回doReturn里的指定值""供调用者使用(可参考下方SpyBeanTest的testException1和testException3)。
(2)Mockito.when(methodService.testException(false)).thenReturn("");
会执行方法体的内容,如果抛出异常会直接中断程序执行,否则不管执行结果是什么,会返回thenReturn里的指定值""供调用者使用。(可参考下方SpyBeanTest的testException和testException2)。
(3)Mockito.doNothing().when(methodService).test();
无返回值方法的代理,调用该方法时什么都不做,即不会执行真实的方法(可参考下方SpyBeanTest的testException5)。
(4)Mockito.when(methodService.testException(true)).thenThrow(new NullPointerException());
代理方法testException,使其执行到此方法时抛出自定义异常(可参考下方SpyBeanTest的testException6)。
@MockBean使用代理控制返回值
(1)Mockito.doReturn("").when(methodService).testException(false);
不会执行方法体的内容,但会返回指定值供调用者使用(可参考下方MockBeanTest的testException1和testException3)。
(2)Mockito.when(methodService.testException(false)).thenReturn("");
不会执行方法体的内容,但会返回指定值供调用者使用(可参考下方MockBeanTest的testException和testException2)。
(3)Mockito.doNothing().when(methodService).test();
无返回值方法的代理,调用该方法时什么都不做,即不会执行真实的方法(可参考下方MockBeanTest的testException5)。
(4)Mockito.when(methodService.testException(true)).thenThrow(new NullPointerException());
代理方法testException,使其执行到此方法时抛出自定义异常(可参考下方MockBeanTest的testException6)。
4、@SpyBean和@MockBean应用测试
定义测试类:
package com.example.mock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class MethodService {
public String test(boolean throwException) {
log.info("test start");
String testException = testException(throwException);
log.info("test end");
return testException;
}
public String testException(boolean throwException) {
log.info("testException start");
if (throwException) {
throw new NullPointerException();
}
log.info("testException end");
return "testException";
}
public void test() {
log.info("test start");
log.info("test end");
}
}
@SpyBean用法
package com.example.mock;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
@SpringBootTest(classes = {MethodService.class})
public class SpyBeanTest {
@SpyBean //如果没有用Mockito设置代理对象控制某个方法的返回值,会执行方法体的内容,代理对象的使用格式一般为Mockito.doReturn("").when(methodService).testException(true);
MethodService methodService;
@Test
void testException() {
Mockito.when(methodService.testException(false)).thenReturn("");//会执行方法体的内容,不管执行结果是什么,都会返回指定值供调用者使用
String testException = methodService.test(false);
Assertions.assertTrue(testException.isEmpty());
}
@Test
void testException1() {
Mockito.doReturn("").when(methodService).testException(false);//不会执行方法体的内容,直接返回指定值供调用者使用
String testException = methodService.test(false);
Assertions.assertTrue(testException.isEmpty());
}
@Test
void testException2() {
Mockito.when(methodService.testException(true)).thenReturn("");//会执行方法体的内容,如果抛出异常会直接中断程序执行,否则不管执行结果是什么,会返回指定值供调用者使用
Assertions.assertThrows(NullPointerException.class, () -> methodService.test(true));
}
@Test
void testException3() {
Mockito.doReturn("").when(methodService).testException(true);//不会执行方法体的内容,直接返回指定值供调用者使用
String testException = methodService.test(true);
Assertions.assertTrue(testException.isEmpty());
}
@Test
void testException4() { //默认会调用真实的方法,有返回值的返回真实的返回值
String testException = methodService.test(false);
Assertions.assertTrue(testException.equals("testException"));
}
@Test
void testException5() { //无返回值方法的代理,调用该方法时什么都不做,即不会执行真实的方法
Mockito.doNothing().when(methodService).test();
methodService.test();
}
@Test
void testException6() {//代理方法testException,使其执行到此方法时抛出自定义异常
Mockito.when(methodService.testException(true)).thenThrow(new NullPointerException());//会执行方法体的内容,如果抛出异常会直接中断程序执行,否则不管执行结果是什么,会返回指定值供调用者使用
Assertions.assertThrows(NullPointerException.class, () -> methodService.test(true));
}
}
@MockBean用法
package com.example.mock;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
@SpringBootTest(classes = {MethodService.class})
public class MockBeanTest {
@MockBean //不管有没有用Mockito设置代理对象返回某个方法的返回值,都不会执行方法体的内容
MethodService methodService;
@Test
void testException() {
Mockito.when(methodService.testException(false)).thenReturn("");//不会执行方法体的内容,但会返回指定值供调用者使用
String testException = methodService.test(false);
Assertions.assertEquals(testException, "");
}
@Test
void testException1() {
Mockito.doReturn("").when(methodService).testException(false);//不会执行方法体的内容,但会返回指定值供调用者使用
String testException = methodService.test(false);
Assertions.assertEquals(testException, "");
}
@Test
void testException2() {
Mockito.when(methodService.testException(true)).thenReturn("");//不会执行方法体的内容,但会返回指定值供调用者使用
String testException = methodService.test(true);
Assertions.assertEquals(testException, "");
}
@Test
void testException3() {
Mockito.doReturn("").when(methodService).testException(true);//不会执行方法体的内容,但会返回指定值供调用者使用
String testException = methodService.test(true);
Assertions.assertEquals(testException, "");
}
@Test
void testException4() {//没有mock的方法不会执行,默认返回null,如调用test方法时,由于没有代理但有返回值,则返回null
Mockito.doReturn("").when(methodService).testException(true);//不会执行方法体的内容,但会返回指定值供调用者使用
String testException = methodService.test(true);
Assertions.assertEquals(testException, "");
}
@Test
void testException5() { //无返回值方法的代理,调用该方法时什么都不做,即不会执行真实的方法
Mockito.doNothing().when(methodService).test();
methodService.test();
}
@Test
void testException6() {//代理方法testException,使其执行到此方法时抛出自定义异常
Mockito.when(methodService.testException(true)).thenThrow(new NullPointerException());//不会执行方法体的内容,但会返回指定值供调用者使用
Assertions.assertThrows(NullPointerException.class, () -> methodService.testException(true));
}
}
5、总结
@MockBean对象,则此对象中所有的方法默认都不会被执行 ,有返回值的会返回null。@SpyBean对象,则此对象中所有的方法默认会调用真实的方法,有返回值的会返回方法的真实返回值。@MockBean或@SpyBean的对象,可使用Mockito代理指定方法返回指定指供调用方使用。
六、扩展知识
@Spy和 @SpyBean的区别, @Mock 和 @MockBean的区别
@Mock和@Spy的对象不受Spring管理;@Spy调用真实方法时,其他bean是无法注入的,若要使用注入,需使用@SpyBean; @SpyBean和@MockBean生成的对象受spring管理,相当于自动替换对应类型bean的注入,比如@Autowired等注入 。
注: @MockBean
只能 mock 本地的代码——或者说是自己写的代码,对于储存在库中而且又是以 Bean 的形式装配到代码中的类无能为力。 @SpyBean
解决了 SpringBoot 的单元测试中 @MockBean
不能 mock 库中自动装配的 Bean 的局限 。