Compare commits

..

4 Commits

Author SHA1 Message Date
金戟
30570ae69d fix: verifier should compare arrays in deep 2023-05-17 19:08:42 +08:00
金戟
320ee632b7 test: add cases of collection tool methods 2022-12-17 08:12:54 +08:00
金戟
ba284f6518 doc: introduce CollectionTool class 2022-12-16 23:09:19 +08:00
金戟
c6e861e5cf refactor: move fastListOf method out of CollectionTool 2022-12-15 09:42:02 +08:00
15 changed files with 455 additions and 30 deletions

View File

@@ -0,0 +1,131 @@
Collection Tools
---
During test data preparation, various collection types are often used. However, collection data initialization in Java needs series of redundant call to the `add()` or `put()` method, making the code very tedious.
Inspired by the collection operations of Kotlin language, `TestableMock` draws on a set of concise collection construction method, which makes the creation of Java collection objects elegant.
The utility class `CollectionTool` provides following global static methods:
## arrayOf
Quickly construct an array of any type, equivalent to Kotlin's `arrayOf()` method.
Usage example:
```java
import static com.alibaba.testable.core.tool.CollectionTool.arrayOf;
// Construct a string array
String[] manyWords = arrayOf("this", "is", "an", "example");
// Construct a double array
Double[] manyValues = arrayOf(1.0D, 2.0D, 3.0D);
```
## listOf
Quickly construct an list of any type, equivalent to Kotlin's `mutableListOf()` method.
Usage example:
```java
import static com.alibaba.testable.core.tool.CollectionTool.listOf;
// Construct a string list
List<String> manyWords = listOf("this", "is", "an", "example");
// Construct a double list
List<Double> manyValues = listOf(1.0D, 2.0D, 3.0D);
```
## setOf
Quickly construct an set of any type, equivalent to Kotlin's `mutableSetOf()` method.
Usage example:
```java
import static com.alibaba.testable.core.tool.CollectionTool.setOf;
// Construct a string set
Set<String> manyWords = setOf("this", "is", "an", "example");
// Construct a double set
Set<Double> manyValues = setOf(1.0D, 2.0D, 3.0D);
```
## mapOf
Quickly construct an map of any type, equivalent to Kotlin's `mutableMapOf()` method.
Usage example:
```java
import static com.alibaba.testable.core.tool.CollectionTool.mapOf;
import static com.alibaba.testable.core.tool.CollectionTool.entryOf;
// Construct a string to string map
Map<String, String> manyWords = mapOf(
entryOf("language", "java"),
entryOf("type", "unittest"),
entryOf("library", "testable")
);
// Construct a integer to double map
Map<Integer, Double> manyValues = mapOf(
entryOf(1, 10.12D),
entryOf(2, 20.24D),
entryOf(3, 30.36D)
);
```
## orderedMapOf
Quickly construct ordered map (LinkedListMap) of any type. Compared with the HashMap map created by `mapOf()`, when traversing its keys or values in a loop, the order will always be the same as entries passed to `orderedMapOf()` method during construction.
Usage example:
```java
import static com.alibaba.testable.core.tool.CollectionTool.orderedMapOf;
import static com.alibaba.testable.core.tool.CollectionTool.entryOf;
// Construct a string to string ordered map
Map<String, String> manyWords = orderedMapOf(
entryOf("language", "java"),
entryOf("type", "unittest"),
entryOf("library", "testable")
);
// Construct a integer to double ordered map
Map<Integer, Double> manyValues = orderedMapOf(
entryOf(1, 10.12D),
entryOf(2, 20.24D),
entryOf(3, 30.36D)
);
```
## entryOf
Used with `mapOf()` and `orderedMapOf()` methods to construct a map.
## slice
Used to extract a part of data from any type of array to a new array.
Usage example:
```java
import static com.alibaba.testable.core.tool.CollectionTool.slice;
import static com.alibaba.testable.core.tool.CollectionTool.arrayOf;
// Extract the elements of the specified string array from subscript 1 to 3
// the returned new array content are ["is", "a", "simple"]
String[] fullWords = arrayOf("this", "is", "a", "simple", "example");
String[] partOfWords = slice(fullWords, 1, 3);
// If only given the index of starting position, all elements after this position are extracted
// the new array returned in the following example is [5, 8, 13, 21]
Integer[] fibonacci = arrayOf(1, 1, 2, 3, 5, 8, 13, 21);
Integer[] partOfFibonacci = slice(fibonacci, 4);
```

View File

@@ -7,6 +7,7 @@ Use TestableMock
- [Access private members of the class under test](en-us/doc/private-accessor.md): enable unit tests directly invoke or access private members of the class under test, solve the problems of private member initialization and private method testing
- [Quickly construct complicated parameter object](en-us/doc/omni-constructor.md)generate arbitrarily nested object instances, simplify their internal member assignment methods, solve the problem of long initialization codes for method parameters
- [Assist test void method](en-us/doc/test-void-method.md): use the mock validator to check the internal logic of method, solve the problem that unit testing is difficult to implement to the method with no return value
- [Quickly create collection instances](en-us/doc/collection-tools.md): provide utility methods for collection creation, solve the problem of redundant codes during java test data preparation
## Use in Maven project

View File

@@ -4,6 +4,7 @@
- [Private Accessor](en-us/doc/private-accessor.md)
- [Omni Constructor](en-us/doc/omni-constructor.md)
- [Test Void Method](en-us/doc/test-void-method.md)
- [Collection Tools](en-us/doc/collection-tools.md)
- Usage Guide
- [Verify Mock Invocation](en-us/doc/invoke-matcher.md)

View File

@@ -20,11 +20,14 @@
- 作用于Mock容器类中的方法
| 参数 | 类型 | 是否必须 | 默认值 | 作用 |
| --- | --- | --- | ---- | --- |
| targetClass | Class | 否 | N/A | 指定Mock目标的调用者类型 |
| targetMethod | String | 否 | N/A | 指定Mock目标的方法名 |
| scope | MockScope | 否 | MockScope.GLOBAL | 指定Mock的生效范围 |
| 参数 | 类型 | 是否必须 | 默认值 | 作用 |
| --- | --- | --- | ---- | --- |
| targetClass | Class | 否 | N/A | 指定Mock目标的调用者类型 |
| targetClassName | String | 否 | N/A | `targetClass`相同,主要用于无法直接引用的私有内部类 |
| targetMethod | String | 否 | N/A | 指定Mock目标的方法名 |
| scope | MockScope | 否 | MockScope.GLOBAL | 指定Mock的生效范围 |
> 说明:`targetClass`和`targetClassName`参数不能同时使用
#### @MockNew

View File

@@ -0,0 +1,129 @@
常用集合构建工具
---
在编写测试数据的时候经常会用到各种集合类型的对象然而Java原生的集合类型需要反复调用`add()``put()`方法来添加数据,使代码显得十分冗长。
`TestableMock`借鉴了Kotlin语言简洁的集合构造方法提供了一个实用的集合构造工具类`CollectionTool`让Java集合对象的创建从此变得优雅。
这个工具类提供了以下全局静态方法:
## arrayOf
快速构造任意类型的数组等效于Kotlin的`arrayOf()`方法。
用法示例:
```java
import static com.alibaba.testable.core.tool.CollectionTool.arrayOf;
// 构造一个字符串数组
String[] manyWords = arrayOf("this", "is", "an", "example");
// 构造一个浮点值数组
Double[] manyValues = arrayOf(1.0D, 2.0D, 3.0D);
```
## listOf
快速构造任意类型的列表等效于Kotlin的`mutableListOf()`方法。
用法示例:
```java
import static com.alibaba.testable.core.tool.CollectionTool.listOf;
// 构造一个字符串列表
List<String> manyWords = listOf("this", "is", "an", "example");
// 构造一个浮点值列表
List<Double> manyValues = listOf(1.0D, 2.0D, 3.0D);
```
## setOf
快速构造任意类型的集合等效于Kotlin的`mutableSetOf()`方法。
用法示例:
```java
import static com.alibaba.testable.core.tool.CollectionTool.setOf;
// 构造一个字符串集合
Set<String> manyWords = setOf("this", "is", "an", "example");
// 构造一个浮点值集合
Set<Double> manyValues = setOf(1.0D, 2.0D, 3.0D);
```
## mapOf
快速构造任意类型的无序映射表等效于Kotlin的`mutableMapOf()`方法。
用法示例:
```java
import static com.alibaba.testable.core.tool.CollectionTool.mapOf;
import static com.alibaba.testable.core.tool.CollectionTool.entryOf;
// 构造一个字符串到字符串的映射
Map<String, String> manyWords = mapOf(
entryOf("language", "java"),
entryOf("type", "unittest"),
entryOf("library", "testable")
);
// 构造一个整型数值到浮点数值的映射
Map<Integer, Double> manyValues = mapOf(
entryOf(1, 10.12D),
entryOf(2, 20.24D),
entryOf(3, 30.36D)
);
```
## orderedMapOf
快速构造任意类型的有序映射表LinkedListMap相较于`mapOf()`创建的HashMap对象当在循环中遍历它的键或值列表时其访问顺序始终会与构造时传给`orderedMapOf()`方法的数据顺序一致。
用法示例:
```java
import static com.alibaba.testable.core.tool.CollectionTool.orderedMapOf;
import static com.alibaba.testable.core.tool.CollectionTool.entryOf;
// 构造一个字符串到字符串的有序映射
Map<String, String> manyWords = orderedMapOf(
entryOf("language", "java"),
entryOf("type", "unittest"),
entryOf("library", "testable")
);
// 构造一个整型数值到浮点数值的有序映射
Map<Integer, Double> manyValues = orderedMapOf(
entryOf(1, 10.12D),
entryOf(2, 20.24D),
entryOf(3, 30.36D)
);
```
## entryOf
配合`mapOf()``orderedMapOf()`方法使用,用于构造映射表。
## slice
用于提取任意类型数组中间的一部分数据,组成新的数组。
用法示例:
```java
import static com.alibaba.testable.core.tool.CollectionTool.slice;
import static com.alibaba.testable.core.tool.CollectionTool.arrayOf;
// 提取指定字符串数组从下标1到3之间的元素得到的新数组内容为["is", "a", "simple"]
String[] fullWords = arrayOf("this", "is", "a", "simple", "example");
String[] partOfWords = slice(fullWords, 1, 3);
// 只传入起始位置下标,提取该位置之后的所有元素,下例返回的新数组内容为[5, 8, 13, 21]
Integer[] fibonacci = arrayOf(1, 1, 2, 3, 5, 8, 13, 21);
Integer[] partOfFibonacci = slice(fibonacci, 4);
```

View File

@@ -7,6 +7,7 @@
- [访问被测类私有成员](zh-cn/doc/private-accessor.md):使单元测试能直接调用和访问被测类的私有成员,解决私有成员初始化和私有方法测试的问题
- [快速构造参数对象](zh-cn/doc/omni-constructor.md):生成任意复杂嵌套的对象实例,并简化其内部成员赋值方式,解决被测方法参数初始化代码冗长的问题
- [辅助测试void方法](zh-cn/doc/test-void-method.md)利用Mock校验器对方法的内部逻辑进行检查解决无返回值方法难以实施单元测试的问题
- [快速构造集合对象](zh-cn/doc/collection-tools.md)提供简洁易用的集合构造和常用操作方法解决准备测试数据时Java集合初始化代码冗长的问题
## 在Maven项目中使用

View File

@@ -4,6 +4,7 @@
- [直接访问私有成员](zh-cn/doc/private-accessor.md)
- [快速构造复杂入参](zh-cn/doc/omni-constructor.md)
- [测试无返回值的方法](zh-cn/doc/test-void-method.md)
- [常用集合构建工具](zh-cn/doc/collection-tools.md)
- 使用指南
- [校验Mock调用](zh-cn/doc/invoke-matcher.md)

View File

@@ -18,7 +18,7 @@ import static com.alibaba.testable.agent.constant.ByteCodeConst.TYPE_CLASS;
import static com.alibaba.testable.agent.constant.ConstPool.*;
import static com.alibaba.testable.agent.util.MockInvokeUtil.*;
import static com.alibaba.testable.core.constant.ConstPool.CONSTRUCTOR;
import static com.alibaba.testable.core.tool.CollectionTool.fastListOf;
import static com.alibaba.testable.core.util.CollectionUtil.fastListOf;
/**
* @author flin

View File

@@ -3,7 +3,7 @@ package com.alibaba.testable.agent.util;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.tree.AnnotationNode;
import static com.alibaba.testable.core.tool.CollectionTool.fastListOf;
import static com.alibaba.testable.core.util.CollectionUtil.fastListOf;
import static org.junit.jupiter.api.Assertions.*;
class AnnotationUtilTest {

View File

@@ -6,6 +6,7 @@ import com.alibaba.testable.core.util.MockContextUtil;
import com.alibaba.testable.core.util.TestableUtil;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.List;
/**
@@ -145,9 +146,13 @@ public class InvocationVerifier {
}
private boolean matches(Object expectValue, Object realValue) {
return expectValue instanceof InvocationMatcher ?
((InvocationMatcher) expectValue).matchFunction.check(realValue) :
expectValue.equals(realValue);
if (expectValue instanceof InvocationMatcher) {
return ((InvocationMatcher) expectValue).matchFunction.check(realValue);
} else if (expectValue.getClass().isArray() && realValue.getClass().isArray()) {
return Arrays.deepEquals((Object[])expectValue, (Object[])realValue);
} else {
return expectValue.equals(realValue);
}
}
private String desc(Object[] args) {

View File

@@ -41,15 +41,6 @@ public class CollectionTool {
return items;
}
/**
* Create an immutable list
* @param items elements to add
* @return list of the provided items
*/
public static <T> List<T> fastListOf(T... items) {
return Arrays.asList(items);
}
/**
* Create a mutable list
* @param items elements to add

View File

@@ -52,4 +52,13 @@ public class CollectionUtil {
return false;
}
/**
* Create an immutable list
* @param items elements to add
* @return list of the provided items
*/
public static <T> List<T> fastListOf(T... items) {
return Arrays.asList(items);
}
}

View File

@@ -0,0 +1,47 @@
package com.alibaba.testable.core.matcher;
import com.alibaba.testable.core.tool.OmniConstructor;
import com.alibaba.testable.core.tool.PrivateAccessor;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.tool.CollectionTool.listOf;
import static org.junit.jupiter.api.Assertions.*;
class InvocationVerifierTest {
private InvocationVerifier invocationVerifier = OmniConstructor.newInstance(InvocationVerifier.class);
@Test
void should_matches_object() {
assertTrue((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches", "abc", "abc"));
assertTrue((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches", 1L, 1L));
assertFalse((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches", "xyz", "abc"));
assertFalse((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches", 1L, "abc"));
assertFalse((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches", 1L, 1));
}
@Test
void should_matches_array() {
assertTrue((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches",
new String[] {"abc", "xyz"}, new String[] {"abc", "xyz"}));
assertFalse((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches",
new String[] {"abc", "xyz"}, new String[] {"xyz", "abc"}));
assertFalse((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches",
new String[] {"abc", "xyz"}, new String[] {"xyz"}));
}
@Test
void should_matches_matcher() {
assertTrue((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches",
InvocationMatcher.anyArray(), new String[] {"abc", "xyz"}));
assertTrue((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches",
InvocationMatcher.anyString(), "abc"));
assertFalse((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches",
InvocationMatcher.anyString(), new String[] {"abc", "xyz"}));
assertFalse((Boolean)PrivateAccessor.invoke(invocationVerifier, "matches",
InvocationMatcher.anyArray(), "abc"));
}
}

View File

@@ -2,22 +2,128 @@ package com.alibaba.testable.core.tool;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.alibaba.testable.core.tool.CollectionTool.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class CollectionToolTest {
@Test
void should_slice_array() {
Object[] args = new Object[]{"1", "2", "3", "4"};
Object[] slicedArgs = CollectionTool.slice(args, 1);
assertEquals(3, slicedArgs.length);
assertEquals("2", slicedArgs[0]);
assertEquals("3", slicedArgs[1]);
assertEquals("4", slicedArgs[2]);
slicedArgs = CollectionTool.slice(args, 1, 2);
assertEquals(2, slicedArgs.length);
assertEquals("2", slicedArgs[0]);
assertEquals("3", slicedArgs[1]);
String[] fullWords = arrayOf("this", "is", "a", "simple", "example");
String[] partOfWords = slice(fullWords, 1, 3);
assertEquals(3, partOfWords.length);
assertEquals("is", partOfWords[0]);
assertEquals("a", partOfWords[1]);
assertEquals("simple", partOfWords[2]);
Integer[] fibonacci = arrayOf(1, 1, 2, 3, 5, 8, 13, 21);
Integer[] partOfFibonacci = slice(fibonacci, 4);
assertEquals(4, partOfFibonacci.length);
assertEquals(5, partOfFibonacci[0]);
assertEquals(8, partOfFibonacci[1]);
assertEquals(13, partOfFibonacci[2]);
assertEquals(21, partOfFibonacci[3]);
}
@Test
void should_create_array() {
String[] manyWords = arrayOf("this", "is", "an", "example");
assertEquals(4, manyWords.length);
assertEquals("this", manyWords[0]);
assertEquals("is", manyWords[1]);
assertEquals("an", manyWords[2]);
assertEquals("example", manyWords[3]);
Double[] manyValues = arrayOf(1.0D, 2.0D, 3.0D);
assertEquals(3, manyValues.length);
assertEquals(1.0D, manyValues[0]);
assertEquals(2.0D, manyValues[1]);
assertEquals(3.0D, manyValues[2]);
}
@Test
void should_create_list() {
List<String> manyWords = listOf("this", "is", "an", "example");
assertEquals(4, manyWords.size());
assertEquals("this", manyWords.get(0));
assertEquals("is", manyWords.get(1));
assertEquals("an", manyWords.get(2));
assertEquals("example", manyWords.get(3));
List<Double> manyValues = listOf(1.0D, 2.0D, 3.0D);
assertEquals(3, manyValues.size());
assertEquals(1.0D, manyValues.get(0));
assertEquals(2.0D, manyValues.get(1));
assertEquals(3.0D, manyValues.get(2));
}
@Test
void should_create_set() {
Set<String> manyWords = setOf("this", "is", "an", "example");
assertEquals(4, manyWords.size());
assertTrue(manyWords.contains("this"));
assertTrue(manyWords.contains("is"));
assertTrue(manyWords.contains("an"));
assertTrue(manyWords.contains("example"));
Set<Double> manyValues = setOf(1.0D, 2.0D, 3.0D);
assertEquals(3, manyValues.size());
assertTrue(manyValues.contains(1.0D));
assertTrue(manyValues.contains(2.0D));
assertTrue(manyValues.contains(3.0D));
}
@Test
void should_create_map() {
Map<String, String> manyWords = mapOf(
entryOf("language", "java"),
entryOf("type", "unittest"),
entryOf("library", "testable")
);
assertEquals(3, manyWords.size());
assertEquals("java", manyWords.get("language"));
assertEquals("unittest", manyWords.get("type"));
assertEquals("testable", manyWords.get("library"));
Map<Integer, Double> manyValues = mapOf(
entryOf(1, 10.12D),
entryOf(2, 20.24D),
entryOf(3, 30.36D)
);
assertEquals(3, manyValues.size());
assertEquals(10.12D, manyValues.get(1));
assertEquals(20.24D, manyValues.get(2));
assertEquals(30.36D, manyValues.get(3));
}
@Test
void should_create_ordered_map() {
Map<String, String> manyWords = orderedMapOf(
entryOf("language", "java"),
entryOf("type", "unittest"),
entryOf("library", "testable")
);
assertEquals(3, manyWords.size());
String[] wordKeys = manyWords.keySet().toArray(new String[0]);
assertEquals("language", wordKeys[0]);
assertEquals("type", wordKeys[1]);
assertEquals("library", wordKeys[2]);
Map<Integer, Double> manyValues = orderedMapOf(
entryOf(1, 10.12D),
entryOf(2, 20.24D),
entryOf(3, 30.36D)
);
assertEquals(3, manyValues.size());
Integer[] valueKeys = manyValues.keySet().toArray(new Integer[0]);
assertEquals(1, valueKeys[0]);
assertEquals(2, valueKeys[1]);
assertEquals(3, valueKeys[2]);
}
}

View File

@@ -2,7 +2,7 @@ package com.alibaba.testable.core.util;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.tool.CollectionTool.fastListOf;
import static com.alibaba.testable.core.util.CollectionUtil.fastListOf;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;