1. JUnit 基本测试类
class StandardTests {
@BeforeAll
static void initAll() {
}
@BeforeEach
void init() {
}
@Test
void succeedingTest() {
}
@Test
@Timeout(value = 100, unit = TimeUnit.MILLISECONDS)
void failingTest() {
fail("a failing test");
}
@Test
@Disabled("for demonstration purposes")
void skippedTest() {
// not executed
}
@Test
void abortedTest() {
assumeTrue("abc".contains("Z"));
fail("test should have been aborted");
}
@AfterEach
void tearDown() {
}
@AfterAll
static void tearDownAll() {
}
}
2.Mockito
Mockito是一个模拟框架,可以按需求控制方法被调用时的返回值或抛出异常操作,还能够对mock对象的操作进行验证。
2.1 简单demo:
LinkedList mockedList = mock(LinkedList.class); //模拟
when(mockedList.get(0)).thenReturn("first"); //存根
when(mockedList.get(1)).thenThrow(new RuntimeException());
System.out.println(mockedList.get(0));
System.out.println(mockedList.get(1));
verify(mockedList).get(0); //验证
2.2 mock()如何实现的
创建一个Mock对象:
LinkedList mockedList = mock(LinkedList.class);
如果调用mockedList.isEmpty()
会返回什么?
//创建Answer
Object returnValueFor(Class<?> type) {
if (Primitives.isPrimitiveOrWrapper(type)) {
return Primitives.defaultValueForPrimitiveOrWrapper(type);
//new instances are used instead of Collections.emptyList(), etc.
//to avoid UnsupportedOperationException if code under test modifies returned collection
} else if (type == Collection.class) {
return new LinkedList<Object>();
} else if (type == Set.class) {
return new HashSet<Object>();
} else if (type == HashSet.class) {
return new HashSet<Object>();
} else if (type == SortedSet.class) {
return new TreeSet<Object>();
} else if (type == TreeSet.class) {
return new TreeSet<Object>();
} else if (type == LinkedHashSet.class) {
return new LinkedHashSet<Object>();
} else if (type == List.class) {
return new LinkedList<Object>();
} else if (type == LinkedList.class) {
return new LinkedList<Object>();
} else if (type == ArrayList.class) {
return new ArrayList<Object>();
} else if (type == Map.class) {
return new HashMap<Object, Object>();
} else if (type == HashMap.class) {
return new HashMap<Object, Object>();
} else if (type == SortedMap.class) {
return new TreeMap<Object, Object>();
} else if (type == TreeMap.class) {
return new TreeMap<Object, Object>();
} else if (type == LinkedHashMap.class) {
return new LinkedHashMap<Object, Object>();
}
// TODO return empty Iterable ; see issue 175
//Let's not care about the rest of collections.
return null;
}
//基本数据类型默认值
static {
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Boolean.class, false);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Character.class, '\u0000');
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Byte.class, (byte) 0);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Short.class, (short) 0);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Integer.class, 0);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Long.class, 0L);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Float.class, 0F);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Double.class, 0D);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(boolean.class, false);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(char.class, '\u0000');
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(byte.class, (byte) 0);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(short.class, (short) 0);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(int.class, 0);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(long.class, 0L);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(float.class, 0F);
PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(double.class, 0D);
}
//创建代理对象
public <T> T imposterise(final MethodInterceptor interceptor, Class<T> mockedType, Class<?>... ancillaryTypes) {
Class<Factory> proxyClass = null;
Object proxyInstance = null;
try {
setConstructorsAccessible(mockedType, true);
proxyClass = createProxyClass(mockedType, ancillaryTypes);
proxyInstance = createProxy(proxyClass, interceptor);
return mockedType.cast(proxyInstance);
} catch (ClassCastException cce) {
throw new MockitoException(join(
"ClassCastException occurred while creating the mockito proxy :",
" class to mock : " + describeClass(mockedType),
" created class : " + describeClass(proxyClass),
" proxy instance class : " + describeClass(proxyInstance),
" instance creation by : " + instantiator.getClass().getSimpleName(),
"",
"You might experience classloading issues, disabling the Objenesis cache *might* help (see MockitoConfiguration)"
), cce);
} finally {
setConstructorsAccessible(mockedType, false);
}
}
2.3 spy半模拟
mock只能mock Class或者Interface,如果想mock一个实例或类进行部分mock则可以使用spy
@Test
public void test1() {
List list = new LinkedList();
list.add("a");
List spy = spy(list);
doReturn("b").when(spy).get(2);
System.out.println(spy.get(0));
System.out.println(spy.get(2));
}
mock和spy有什么区别?
static class A {
int function1() {
System.out.println("this is function 1");
return 1;
}
int function2() {
System.out.println("this is function 2");
return 2;
}
}
@Test
public void differenceBetweenMockAndSpy() {
A mockA = mock(A.class);
A spyA = spy(A.class);
System.out.println(mockA.function1());
System.out.println();
System.out.println(spyA.function1());
}
在半模拟中,未指定的stub会自动执行callRealMethod()
。
2.4 存根方式区别
Mockito有两种存根语法,do...when...action
、when...then
,两种语法在mock全模拟情况下作用相同,但是在spy半模拟情况下效果不同:
static class A {
int function1() {
System.out.println("this is function 1");
return 1;
}
int function2() {
System.out.println("this is function 2");
return 2;
}
}
@Test
public void differenceBetweenWhenThenAndDoWhen() {
A mockA = spy(A.class);
when(mockA.function1()).thenReturn(99);
doReturn(88).when(mockA).function2();
System.out.println(mockA.function1());
System.out.println();
System.out.println(mockA.function2());
}
2.5 与SpringBoot结合
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
测试类
@Autowired
private WebApplicationContext applicationContext;
//实现对容器Bean的mock
@MockBean
private Service1 service1;
//创建mockMvc
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
}
@Test
public void test1(){
//mock service1,如果/test1的controller调用了service1,则会返回999
when(service1.query(any())).thenReturn(999);
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test1")
.header("Date", dateStr)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(requestBodyJson)
}