目录
一、概述
二、基本校验器
三、基本匹配器
四、空值匹配器
五、字符串匹配器
六、万能匹配器
七、使用示例
一、概述
在测试中,除了需要将某些含有外部依赖的方法替换为Mock,经常还会需要验证该方法被调用时的参数是否符合预期。
在TestableMock中提供了校验器(verifier)和匹配器(matcher)来实现这一功能。譬如:
@Test
public test_case() {
int res = insToTest.methodToTest();
verify("mockMethod").with(123, "abc");
}
这个用例会检查在执行被测方法methodToTest()时,名称是mockMethod的Mock方法是否有被调用过,且调用时收到的参数值是否为123和"abc"(假设被Mock的mockMethod方法有两个参数)。
除了这种简单校验以外,TestableMock当前已经支持了多种校验器,以及能够模糊匹配参数特征的匹配器。
二、基本校验器
- with(Object... args) → 验证方法是否被指定参数调用过;
- withInOrder(Object... args) → 如果指定方法被调用了多次,依据实际调用顺序依次匹配;
- withTimes(int expectedCount) → 验证方法是否被调用过指定次数,忽略对调用参数的检查;
- without(Object... args) → 验证方法从未被使用指定参数调用过;
- times(int count) → 连在with()或withInOrder()方法之后使用,验证该方法被同样条件的参数调用过了指定次数;
三、基本匹配器
- any() → 匹配任何值,包括Null
- any(Class> clazz) → 匹配任何指定类型或子类型的值
- anyTypeOf(Class>... classes) → 匹配在列表中的任意一种类型的值
- anyString() → 匹配任何字符串
- anyNumber() → 匹配任何数值(整数或浮点数)
- anyBoolean() → 匹配任何布尔值
- anyByte() → 匹配任何单字节类型的值
- anyChar() → 匹配任何单字符类型的值
- anyInt() → 匹配任何整数类型的值
- anyLong() → 匹配任何长整数类型的值
- anyFloat() → 匹配任何浮点数类型的值
- anyDouble() → 匹配任何双精度浮点数类型的值
- anyShort() → 匹配任何短整数类型的值
- anyArray() → 匹配任何数组
- anyArrayOf(Class> clazz) → 匹配任何指定类型的数组
- anyList() → 匹配任何列表
- anyListOf(Class> clazz) → 匹配任何指定类型的列表
- anySet() → 匹配任何集合
- anySetOf(Class> clazz) → 匹配任何指定类型的集合
- anyMap() → 匹配任何映射
- anyMapOf(Class> keyClass, Class> valueClass) → 匹配任何指定类型的映射
- anyCollection() → 匹配任何容器
- anyCollectionOf(Class> clazz) → 匹配任何指定类型的容器
- anyIterable() → 匹配任何迭代器
- anyIterableOf(Class> clazz) → 匹配任何指定类型的迭代器
- eq(Object obj) → 匹配与指定值相等的对象
- refEq(Object obj) → 匹配指定对象(非值相等,而是就是同一个对象)
四、空值匹配器
- isNull() → 匹配Null
- notNull() → 匹配除Null以外的任何值
- nullable(Class> clazz) → 匹配空或指定类型的任何值
五、字符串匹配器
- contains(String substring) → 匹配包含特定子串的字符串
- matches(String regex) → 匹配符合指定正则表达式的字符串
- endsWith(String suffix) → 匹配以指定子串结尾的字符串
- startsWith(String prefix) → 匹配以指定子串开头的字符串
六、万能匹配器
- any(MatchFunction matcher) → 匹配符合指定表达式的值
七、使用示例
- any() → 匹配任何值,包括Null
- any(Class> clazz) → 匹配任何指定类型或子类型的值
- anyTypeOf(Class>... classes) → 匹配在列表中的任意一种类型的值
- anyString() → 匹配任何字符串
- anyNumber() → 匹配任何数值(整数或浮点数)
- anyBoolean() → 匹配任何布尔值
- anyByte() → 匹配任何单字节类型的值
- anyChar() → 匹配任何单字符类型的值
- anyInt() → 匹配任何整数类型的值
- anyLong() → 匹配任何长整数类型的值
- anyFloat() → 匹配任何浮点数类型的值
- anyDouble() → 匹配任何双精度浮点数类型的值
- anyShort() → 匹配任何短整数类型的值
- anyArray() → 匹配任何数组
- anyArrayOf(Class> clazz) → 匹配任何指定类型的数组
- anyList() → 匹配任何列表
- anyListOf(Class> clazz) → 匹配任何指定类型的列表
- anySet() → 匹配任何集合
- anySetOf(Class> clazz) → 匹配任何指定类型的集合
- anyMap() → 匹配任何映射
- anyMapOf(Class> keyClass, Class> valueClass) → 匹配任何指定类型的映射
- anyCollection() → 匹配任何容器
- anyCollectionOf(Class> clazz) → 匹配任何指定类型的容器
- anyIterable() → 匹配任何迭代器
- anyIterableOf(Class> clazz) → 匹配任何指定类型的迭代器
- eq(Object obj) → 匹配与指定值相等的对象
- refEq(Object obj) → 匹配指定对象(非值相等,而是就是同一个对象)
四、空值匹配器
- isNull() → 匹配Null
- notNull() → 匹配除Null以外的任何值
- nullable(Class> clazz) → 匹配空或指定类型的任何值
五、字符串匹配器
- contains(String substring) → 匹配包含特定子串的字符串
- matches(String regex) → 匹配符合指定正则表达式的字符串
- endsWith(String suffix) → 匹配以指定子串结尾的字符串
- startsWith(String prefix) → 匹配以指定子串开头的字符串
六、万能匹配器
- any(MatchFunction matcher) → 匹配符合指定表达式的值
七、使用示例
- contains(String substring) → 匹配包含特定子串的字符串
- matches(String regex) → 匹配符合指定正则表达式的字符串
- endsWith(String suffix) → 匹配以指定子串结尾的字符串
- startsWith(String prefix) → 匹配以指定子串开头的字符串
六、万能匹配器
- any(MatchFunction matcher) → 匹配符合指定表达式的值
七、使用示例
【a】编写被测试类
package com.wsh.testable.mock.testablemockdemo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
public class DemoMatcher {
private void methodToBeMocked() {
// pretend to have some code here
}
private void methodToBeMocked(Object a1, Object a2) {
// pretend to have some code here
}
private void methodToBeMocked(Object[] a) {
// pretend to have some code here
}
public void callMethodWithoutArgument() {
methodToBeMocked();
}
public void callMethodWithNumberArguments() {
// named variable and lambda variable will be recorded as different type
// should have them both in test case
List floatList = new ArrayList<>();
floatList.add(1.0F);
floatList.add(2.0F);
Long[] longArray = new Long[]{1L, 2L};
methodToBeMocked(1, 2);
methodToBeMocked(1L, 2.0);
methodToBeMocked(new ArrayList(){{ add(1); }}, new HashSet(){{ add(1.0F); }});
methodToBeMocked(1.0, new HashMap(2){{ put(1, 1.0F); }});
methodToBeMocked(floatList, floatList);
methodToBeMocked(longArray);
methodToBeMocked(new Double[]{1.0, 2.0});
}
public void callMethodWithStringArgument() {
methodToBeMocked("hello", "world");
methodToBeMocked("testable", "mock");
methodToBeMocked(new String[]{"demo"});
}
public void callMethodWithObjectArgument() {
methodToBeMocked(new BlackBox("hello"), new BlackBox("world"));
methodToBeMocked(new BlackBox("demo"), null);
methodToBeMocked(null, new BlackBox("demo"));
}
}
【b】编写测试类
package com.wsh.testable.mock.testablemockdemo;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.core.error.VerifyFailedError;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvokeMatcher.any;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyArray;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyArrayOf;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyInt;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyList;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyListOf;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyLong;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyMapOf;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyNumber;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anySetOf;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyString;
import static com.alibaba.testable.core.matcher.InvokeMatcher.contains;
import static com.alibaba.testable.core.matcher.InvokeMatcher.endsWith;
import static com.alibaba.testable.core.matcher.InvokeMatcher.isNull;
import static com.alibaba.testable.core.matcher.InvokeMatcher.matches;
import static com.alibaba.testable.core.matcher.InvokeMatcher.notNull;
import static com.alibaba.testable.core.matcher.InvokeMatcher.nullable;
import static com.alibaba.testable.core.matcher.InvokeMatcher.startsWith;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
import static org.junit.jupiter.api.Assertions.fail;
class DemoMatcherTest {
private DemoMatcher demoMatcher = new DemoMatcher();
public static class Mock {
@MockMethod(targetMethod = "methodToBeMocked")
private void methodWithoutArgument(DemoMatcher self) {
}
@MockMethod(targetMethod = "methodToBeMocked")
private void methodWithArguments(DemoMatcher self, Object a1, Object a2) {
}
@MockMethod(targetMethod = "methodToBeMocked")
private void methodWithArrayArgument(DemoMatcher self, Object[] a) {
}
}
@Test
void should_match_no_argument() {
//callMethodWithoutArgument()方法内部调用了methodToBeMocked()方法
demoMatcher.callMethodWithoutArgument();
//检查在执行被测方法callMethodWithoutArgument()时,名称是methodWithoutArgument的Mock方法是否有被调用过一次,并忽略对调用参数的检查
verify("methodWithoutArgument").withTimes(1);
demoMatcher.callMethodWithoutArgument();
//检查在执行被测方法callMethodWithoutArgument()时,名称是methodWithoutArgument的Mock方法是否有被调用过两次,并忽略对调用参数的检查
verify("methodWithoutArgument").withTimes(2);
}
@Test
void should_match_number_arguments() {
//callMethodWithNumberArguments()方法内部调用了methodToBeMocked()很多重载的方法
demoMatcher.callMethodWithNumberArguments();
//验证Mock方法methodWithArguments()从未被使用指定参数调用过
verify("methodWithArguments").without(anyString(), 2);
//withInOrder(Object... args) → 如果指定方法被调用了多次,依据实际调用顺序依次匹配
verify("methodWithArguments").withInOrder(anyInt(), 2);
verify("methodWithArguments").withInOrder(anyLong(), anyNumber());
//验证Mock方法methodWithArguments()是否被指定参数调用过
verify("methodWithArguments").with(1.0, anyMapOf(Integer.class, Float.class));
verify("methodWithArguments").with(anyList(), anySetOf(Float.class));
verify("methodWithArguments").with(anyList(), anyListOf(Float.class));
//验证Mock方法methodWithArrayArgument()是否被指定参数调用过
verify("methodWithArrayArgument").with(anyArrayOf(Long.class));
verify("methodWithArrayArgument").with(anyArray());
}
@Test
void should_match_string_arguments() {
//注意callMethodWithStringArgument()在类DemoMatcher中的实现
demoMatcher.callMethodWithStringArgument();
//验证Mock方法methodWithArguments()是否被指定参数调用过,参数分别满足以"he"开头、以"ld"结尾
verify("methodWithArguments").with(startsWith("he"), endsWith("ld"));
//参数类型:包含"stab"、匹配"m.[cd]k"正则表达式
verify("methodWithArguments").with(contains("stab"), matches("m.[cd]k"));
//参数类型:String类型的数组
verify("methodWithArrayArgument").with(anyArrayOf(String.class));
}
@Test
void should_match_object_arguments() {
demoMatcher.callMethodWithObjectArgument();
verify("methodWithArguments").withInOrder(any(BlackBox.class), any(BlackBox.class));
verify("methodWithArguments").withInOrder(nullable(BlackBox.class), nullable(BlackBox.class));
verify("methodWithArguments").withInOrder(isNull(), notNull());
}
@Test
void should_match_with_times() {
demoMatcher.callMethodWithNumberArguments();
verify("methodWithArguments").with(anyNumber(), any()).times(3);
demoMatcher.callMethodWithNumberArguments();
boolean gotError = false;
try {
verify("methodWithArguments").with(anyNumber(), any()).times(4);
} catch (VerifyFailedError e) {
gotError = true;
}
if (!gotError) {
fail();
}
}
}
本案例来自官网, 笔者只是做一个学习总结。更详细使用介绍参考官网:TestableMock



