refactor test code, and omni constructor test

This commit is contained in:
金戟 2021-03-18 23:51:06 +08:00
parent 206fc5486e
commit 606760d632
42 changed files with 364 additions and 148 deletions

View File

@ -1,11 +1,8 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.Box;
import com.alibaba.demo.basic.model.Color;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.Box;
import com.alibaba.demo.basic.model.Color;
import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.demo.basic.model.mock.Box;
import com.alibaba.demo.basic.model.mock.Color;
/**
* 演示父类变量引用子类对象时的Mock场景

View File

@ -1,8 +1,7 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.mock.BlackBox;
import java.util.ArrayList;
import java.util.HashMap;

View File

@ -1,7 +1,6 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.mock.BlackBox;
import java.nio.file.Files;
import java.nio.file.Paths;

View File

@ -1,4 +1,4 @@
package com.alibaba.demo.basic.model;
package com.alibaba.demo.basic.model.mock;
public class BlackBox extends Box implements Color {

View File

@ -1,4 +1,4 @@
package com.alibaba.demo.basic.model;
package com.alibaba.demo.basic.model.mock;
abstract public class Box {

View File

@ -1,4 +1,4 @@
package com.alibaba.demo.basic.model;
package com.alibaba.demo.basic.model.mock;
public interface Color {

View File

@ -0,0 +1,43 @@
package com.alibaba.demo.basic.model.omni;
public class Child {
/**
* An inner class
*/
public class SubChild {
private GrandChild gc;
public GrandChild getGrandChild() {
return gc;
}
public void setGrandChild(GrandChild grandChild) {
this.gc = grandChild;
}
}
// ---------- Member fields ----------
private GrandChild gc;
private EnumChild ec;
// ---------- Getters and Setters ----------
public GrandChild getGrandChild() {
return gc;
}
public void setGrandChild(GrandChild grandChild) {
this.gc = grandChild;
}
public EnumChild getEnumChild() {
return ec;
}
public void setEnumChild(EnumChild enumChild) {
this.ec = enumChild;
}
}

View File

@ -0,0 +1,15 @@
package com.alibaba.demo.basic.model.omni;
public enum EnumChild {
/**
* demo value - 1
*/
VAL1,
/**
* demo value - 2
*/
VAL2
}

View File

@ -0,0 +1,22 @@
package com.alibaba.demo.basic.model.omni;
public class GrandChild {
// ---------- Member fields ----------
private int value;
// ---------- Constructor, Getters and Setters ----------
public GrandChild(int v) {
this.value = v;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}

View File

@ -0,0 +1,39 @@
package com.alibaba.demo.basic.model.omni;
public class Parent {
// ---------- Member fields ----------
private Child c;
private Child[] cs;
private Child.SubChild sc;
// ---------- Getters and Setters ----------
public Child getChild() {
return c;
}
public void setChild(Child child) {
this.c = child;
}
public Child[] getChildren() {
return cs;
}
public void setChildren(Child[] children) {
this.cs = children;
}
public Child.SubChild getSubChild() {
return sc;
}
public void setSubChild(Child.SubChild subChild) {
this.sc = subChild;
}
}

View File

@ -1,13 +1,9 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.Box;
import com.alibaba.demo.basic.model.Color;
import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.demo.basic.model.mock.Box;
import com.alibaba.demo.basic.model.mock.Color;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.demo.basic.DemoInherit;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.Box;
import com.alibaba.demo.basic.model.Color;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
@ -54,42 +50,42 @@ class DemoInheritTest {
}
@Test
void should_able_to_mock_call_sub_object_method_by_parent_object() {
void should_mock_call_sub_object_method_by_parent_object() {
BlackBox box = (BlackBox)demoInherit.putIntoBox();
verify("put_into_box").withTimes(1);
assertEquals("put_data_into_box", box.get());
}
@Test
void should_able_to_mock_call_sub_object_method_by_sub_object() {
void should_mock_call_sub_object_method_by_sub_object() {
BlackBox box = demoInherit.putIntoBlackBox();
verify("put_into_blackbox").withTimes(1);
assertEquals("put_data_into_blackbox", box.get());
}
@Test
void should_able_to_mock_call_parent_object_method_by_parent_object() {
void should_mock_call_parent_object_method_by_parent_object() {
String content = demoInherit.getFromBox();
verify("get_from_box").withTimes(1);
assertEquals("get_from_box", content);
}
@Test
void should_able_to_mock_call_parent_object_method_by_sub_object() {
void should_mock_call_parent_object_method_by_sub_object() {
String content = demoInherit.getFromBlackBox();
verify("get_from_blackbox").withTimes(1);
assertEquals("get_from_blackbox", content);
}
@Test
void should_able_to_mock_call_interface_method_by_interface_object() {
void should_mock_call_interface_method_by_interface_object() {
String color = demoInherit.getColorViaColor();
verify("get_color_from_color").withTimes(1);
assertEquals("color_from_color", color);
}
@Test
void should_able_to_mock_call_interface_method_by_sub_class_object() {
void should_mock_call_interface_method_by_sub_class_object() {
String color = demoInherit.getColorViaBox();
verify("get_color_from_blackbox").withTimes(1);
assertEquals("color_from_blackbox", color);

View File

@ -19,7 +19,7 @@ class DemoInnerClassTest {
}
@Test
void should_able_to_mock_invoke_inside_inner_class() throws Exception {
void should_mock_invoke_inside_inner_class() throws Exception {
DemoInnerClass demo = new DemoInnerClass();
assertEquals("MockedCall", demo.callInnerDemo());
assertEquals("MockedCall", demo.callAnonymousInner());

View File

@ -1,10 +1,8 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.core.error.VerifyFailedError;
import com.alibaba.demo.basic.DemoMatcher;
import com.alibaba.demo.basic.model.BlackBox;
import org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvokeMatcher.*;

View File

@ -1,9 +1,8 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.testable.core.annotation.MockConstructor;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.demo.basic.model.BlackBox;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -73,20 +72,20 @@ class DemoMockTest {
}
@Test
void should_able_to_mock_new_object() {
void should_mock_new_object() {
assertEquals("mock_something", demoMock.newFunc());
verify("createBlackBox").with("something");
}
@Test
void should_able_to_mock_member_method() throws Exception {
void should_mock_member_method() throws Exception {
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"));
verify("innerFunc").with("hello");
verify("staticFunc").with();
}
@Test
void should_able_to_mock_common_method() {
void should_mock_common_method() {
assertEquals("trim_string__sub_string__false", demoMock.commonFunc());
verify("trim").withTimes(1);
verify("sub").withTimes(1);
@ -94,13 +93,13 @@ class DemoMockTest {
}
@Test
void should_able_to_mock_static_method() {
void should_mock_static_method() {
Assertions.assertEquals("not_secret_box", demoMock.getBox().get());
verify("secretBox").withTimes(1);
}
@Test
void should_able_to_get_source_method_name() throws Exception {
void should_get_source_method_name() throws Exception {
// synchronous
assertEquals("mock_one_mock_others", demoMock.callerOne() + "_" + demoMock.callerTwo());
// asynchronous
@ -110,7 +109,7 @@ class DemoMockTest {
}
@Test
void should_able_to_set_mock_context() throws Exception {
void should_set_mock_context() throws Exception {
MOCK_CONTEXT.put("case", "special_case");
// synchronous
assertEquals("mock_special", demoMock.callerOne());

View File

@ -0,0 +1,54 @@
package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.omni.Child;
import com.alibaba.demo.basic.model.omni.EnumChild;
import com.alibaba.demo.basic.model.omni.GrandChild;
import com.alibaba.demo.basic.model.omni.Parent;
import com.alibaba.testable.core.tool.OmniAccessor;
import com.alibaba.testable.core.tool.OmniConstructor;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* 演示快速创建任意对象和使用路径访问成员
* Demonstrate quick object construction and access members by path
*/
class DemoOmniMethodsTest {
@Test
void should_construct_any_class() {
// 所有基础类型初始化为默认数值
GrandChild aGrandChild = OmniConstructor.newInstance(GrandChild.class);
assertEquals(0, aGrandChild.getValue());
// 所有枚举类型初始化为第一个可选值
Child aChild = OmniConstructor.newInstance(Child.class);
assertEquals(EnumChild.VAL1, aChild.getEnumChild());
// 所有数组类型初始化为空数组
// 所有子孙成员对象都会逐级初始化
Parent aParent = OmniConstructor.newInstance(Parent.class);
assertEquals(0, aParent.getChildren().length);
assertEquals(0, aParent.getSubChild().getGrandChild().getValue());
}
@Test
void should_get_any_member() {
Parent demo = OmniConstructor.newInstance(Parent.class);
demo.getChild().setEnumChild(EnumChild.VAL2);
assertEquals(EnumChild.VAL2, OmniAccessor.get(demo, "ec"));
assertEquals(EnumChild.VAL2, OmniAccessor.get(demo, "{EnumChild}"));
assertEquals(EnumChild.VAL2, OmniAccessor.get(demo, "c/ec"));
demo.setChildren(new Child[]{ new Child() });
assertEquals(EnumChild.VAL1, OmniAccessor.get(demo, "cs[0]/ec"));
}
@Test
void should_set_any_member() {
}
}

View File

@ -21,7 +21,7 @@ class DemoPrivateAccessTest {
private DemoPrivateAccess demoPrivateAccess = new DemoPrivateAccess();
@Test
void should_able_to_access_private_method() {
void should_access_private_method() {
List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
assertEquals("member", demoPrivateAccess.privateFunc());
assertEquals("member", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc"));
@ -30,7 +30,7 @@ class DemoPrivateAccessTest {
}
@Test
void should_able_to_access_private_field() {
void should_access_private_field() {
demoPrivateAccess.count = 2;
assertEquals(Integer.valueOf(2), demoPrivateAccess.count);
@ -39,7 +39,7 @@ class DemoPrivateAccessTest {
}
@Test
void should_able_to_access_private_static_method() {
void should_access_private_static_method() {
assertEquals("static", DemoPrivateAccess.privateStaticFunc());
assertEquals("static", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFunc"));
assertEquals("hello + 1", DemoPrivateAccess.privateStaticFuncWithArgs("hello", 1));
@ -47,7 +47,7 @@ class DemoPrivateAccessTest {
}
@Test
void should_able_to_access_private_static_field() {
void should_access_private_static_field() {
DemoPrivateAccess.staticCount = 2;
assertEquals(Integer.valueOf(2), DemoPrivateAccess.staticCount);
@ -56,7 +56,7 @@ class DemoPrivateAccessTest {
}
@Test
void should_able_to_update_final_field() {
void should_update_final_field() {
demoPrivateAccess.pi = 4.13;
assertEquals(Double.valueOf(4.13), demoPrivateAccess.pi);
@ -65,7 +65,7 @@ class DemoPrivateAccessTest {
}
@Test
void should_able_to_use_null_parameter() {
void should_use_null_parameter() {
demoPrivateAccess.pi = null;
assertNull(demoPrivateAccess.pi);
assertEquals("null + 1", DemoPrivateAccess.privateStaticFuncWithArgs(null, 1));

View File

@ -72,19 +72,19 @@ class DemoTemplateTest {
}
@Test
void should_able_to_mock_single_template_method() {
void should_mock_single_template_method() {
String res = demoTemplate.singleTemplateMethod();
assertEquals("demo_mock_list", res);
}
@Test
void should_able_to_mock_double_template_method() {
void should_mock_double_template_method() {
String res = demoTemplate.doubleTemplateMethod();
assertEquals("testable_mock_map", res);
}
@Test
void should_able_to_mock_new_template_method() {
void should_mock_new_template_method() {
Set<?> res = demoTemplate.newTemplateMethod();
assertEquals(2, res.size());
Iterator<?> iterator = res.stream().iterator();

View File

@ -15,7 +15,7 @@ public class OneToMultiSvcTest {
private CSvc cSvc = new CSvc();
@Test
public void should_able_to_test_multi_class_together() {
public void should_test_multi_class_together() {
assertEquals("a_mock", aSvc.demo("test"));
assertEquals("b_mock", bSvc.demo("test"));
assertEquals("c_mock", cSvc.demo("test"));

View File

@ -49,42 +49,42 @@ internal class DemoInheritTest {
}
@Test
fun should_able_to_mock_call_sub_object_method_by_parent_object() {
fun should_mock_call_sub_object_method_by_parent_object() {
val box = demoInherit.putIntoBox() as BlackBox
InvokeVerifier.verify("put_into_box").withTimes(1)
Assertions.assertEquals("put_data_into_box", box.get())
}
@Test
fun should_able_to_mock_call_sub_object_method_by_sub_object() {
fun should_mock_call_sub_object_method_by_sub_object() {
val box = demoInherit.putIntoBlackBox()
InvokeVerifier.verify("put_into_blackbox").withTimes(1)
Assertions.assertEquals("put_data_into_blackbox", box.get())
}
@Test
fun should_able_to_mock_call_parent_object_method_by_parent_object() {
fun should_mock_call_parent_object_method_by_parent_object() {
val content = demoInherit.fromBox
InvokeVerifier.verify("get_from_box").withTimes(1)
Assertions.assertEquals("get_from_box", content)
}
@Test
fun should_able_to_mock_call_parent_object_method_by_sub_object() {
fun should_mock_call_parent_object_method_by_sub_object() {
val content = demoInherit.fromBlackBox
InvokeVerifier.verify("get_from_blackbox").withTimes(1)
Assertions.assertEquals("get_from_blackbox", content)
}
@Test
fun should_able_to_mock_call_interface_method_by_interface_object() {
fun should_mock_call_interface_method_by_interface_object() {
val color = demoInherit.colorViaColor
InvokeVerifier.verify("get_color_from_color").withTimes(1)
Assertions.assertEquals("color_from_color", color)
}
@Test
fun should_able_to_mock_call_interface_method_by_sub_class_object() {
fun should_mock_call_interface_method_by_sub_class_object() {
val color = demoInherit.colorViaBox
InvokeVerifier.verify("get_color_from_blackbox").withTimes(1)
Assertions.assertEquals("color_from_blackbox", color)

View File

@ -19,7 +19,7 @@ internal class DemoInnerClassTest {
@Test
@Throws(Exception::class)
fun should_able_to_mock_invoke_inside_inner_class() {
fun should_mock_invoke_inside_inner_class() {
val demo = DemoInnerClass()
Assertions.assertEquals("MockedCall", demo.callInnerDemo())
Assertions.assertEquals("MockedCall", demo.callAnonymousInner())

View File

@ -64,26 +64,26 @@ internal class DemoMockTest {
}
@Test
fun should_able_to_mock_new_object() {
fun should_mock_new_object() {
assertEquals("mock_something", demoMock.newFunc())
verify("createBlackBox").with("something")
}
@Test
fun should_able_to_mock_member_method() {
fun should_mock_member_method() {
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"))
verify("innerFunc").with("hello")
verify("staticFunc").with()
}
// @Test
// fun should_able_to_mock_method_in_companion_object() {
// fun should_mock_method_in_companion_object() {
// assertEquals("CALL_MOCK_TAIL", DemoMock.callStaticFunc())
// verify("staticFunc").with()
// }
@Test
fun should_able_to_mock_common_method() {
fun should_mock_common_method() {
assertEquals("trim_string__sub_string__false", demoMock.commonFunc())
verify("trim").withTimes(1)
verify("sub").withTimes(1)
@ -91,14 +91,14 @@ internal class DemoMockTest {
}
@Test
fun should_able_to_mock_static_method() {
fun should_mock_static_method() {
assertEquals("White_not_secret_box", demoMock.getBox().get())
verify("secretBox").withTimes(1)
verify("createBox").withTimes(1)
}
@Test
fun should_able_to_get_source_method_name() {
fun should_get_source_method_name() {
// synchronous
assertEquals("mock_one_mock_others", demoMock.callerOne() + "_" + demoMock.callerTwo())
// asynchronous
@ -109,7 +109,7 @@ internal class DemoMockTest {
}
@Test
fun should_able_to_get_test_case_name() {
fun should_get_test_case_name() {
MOCK_CONTEXT["case"] = "special_case"
// synchronous
assertEquals("mock_special", demoMock.callerOne())

View File

@ -13,32 +13,32 @@ internal class DemoPrivateAccessTest {
private val demoPrivateAccess = DemoPrivateAccess()
@Test
fun should_able_to_access_private_method() {
fun should_access_private_method() {
val list = listOf("a", "b", "c");
assertEquals("abc + hello + 1", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc", list, "hello", 1))
}
@Test
fun should_able_to_access_private_field() {
fun should_access_private_field() {
PrivateAccessor.set(demoPrivateAccess, "count", 3)
assertEquals(3, PrivateAccessor.get(demoPrivateAccess, "count"))
}
@Test
fun should_able_to_access_private_static_method() {
fun should_access_private_static_method() {
val list = listOf("a", "b", "c");
assertEquals("hello + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess::class.java, "privateStaticFunc", "hello", 1))
assertEquals("abc * hello * 1", PrivateAccessor.invokeStatic(DemoPrivateAccess::class.java, "privateJvmStaticFunc", list, "hello", 1))
}
@Test
fun should_able_to_access_private_static_field() {
fun should_access_private_static_field() {
PrivateAccessor.setStatic(DemoPrivateAccess::class.java, "staticCount", 3)
assertEquals(3, PrivateAccessor.getStatic(DemoPrivateAccess::class.java, "staticCount"))
}
@Test
fun should_able_to_update_final_field() {
fun should_update_final_field() {
PrivateAccessor.set(demoPrivateAccess, "pi", 4.13)
assertEquals(4.13, demoPrivateAccess.pi)
}

View File

@ -40,19 +40,19 @@ internal class DemoTemplateTest {
}
@Test
fun should_able_to_mock_single_template_method() {
fun should_mock_single_template_method() {
val res = demoTemplate.singleTemplateMethod()
Assertions.assertEquals("demo_mock_list", res)
}
@Test
fun should_able_to_mock_double_template_method() {
fun should_mock_double_template_method() {
val res = demoTemplate.doubleTemplateMethod()
Assertions.assertEquals("testable_mock_map", res)
}
@Test
fun should_able_to_mock_new_template_method() {
fun should_mock_new_template_method() {
val res = demoTemplate.newTemplateMethod()
Assertions.assertEquals(2, res.size)
val iterator = res.stream().iterator()

View File

@ -41,7 +41,7 @@ class PathUtilTest {
}
@Test
fun should_able_to_mock_java_method_invoke_in_kotlin() {
fun should_mock_java_method_invoke_in_kotlin() {
PathUtil.deleteRecursively(File("/a/b/"))
verify("listFiles").withTimes(2)
verify("delete").withTimes(4)

View File

@ -13,7 +13,7 @@ class OneToMultiSvcTest {
private val cSvc = CSvc()
@Test
fun should_able_to_test_multi_class_together() {
fun should_test_multi_class_together() {
Assertions.assertEquals("a_mock", aSvc.demo("test"))
Assertions.assertEquals("b_mock", bSvc.demo("test"))
Assertions.assertEquals("c_mock", cSvc.demo("test"))

View File

@ -54,9 +54,9 @@ Executing the unit test again will print out the signatures of all mock methods,
```text
[DIAGNOSE] Handling test class com/alibaba/testable/demo/basic/DemoMockTest
[VERBOSE] Test case "should_able_to_mock_new_object"
[VERBOSE] Test case "should_mock_new_object"
... ...
[VERBOSE] Test case "should_able_to_set_mock_context"
[VERBOSE] Test case "should_set_mock_context"
[DIAGNOSE] Found 6 test cases
[DIAGNOSE] Handling mock class com/alibaba/testable/demo/basic/DemoMockTest$Mock
[VERBOSE] Mock constructor "createBlackBox" as "com.alibaba.demo.basic.model.BlackBox(java.lang.String)"

View File

@ -19,7 +19,7 @@ public class DemoMockTest {
}
@Test
void should_able_to_mock_member_method() throws Exception {
void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world");
}
@ -39,7 +39,7 @@ public class DemoMockTest {
} // Add this line
@Test
void should_able_to_mock_member_method() throws Exception {
void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world");
}

View File

@ -67,7 +67,7 @@ private String substring(String self, int i, int j) {
}
```
For complete code examples, see the `should_able_to_mock_common_method()` test cases in the `java-demo` and `kotlin-demo` sample projects. (Because Kotlin has made magical changes to the String type, the method under test in the Kotlin example adds a layer of encapsulation to the `BlackBox` class)
For complete code examples, see the `should_mock_common_method()` test cases in the `java-demo` and `kotlin-demo` sample projects. (Because Kotlin has made magical changes to the String type, the method under test in the Kotlin example adds a layer of encapsulation to the `BlackBox` class)
#### 2. Mock the member method of the class under test itself
@ -87,7 +87,7 @@ private String innerFunc(String text) {
Similarly, if the method in the above example needs to access the original tested object that initiated the call, it may not use the `targetClass` parameter, but when defining the mock method, add a parameter of type `DemoMock` to the first index of the method parameter list.
For complete code examples, see the `should_able_to_mock_member_method()` test case in the `java-demo` and `kotlin-demo` sample projects.
For complete code examples, see the `should_mock_member_method()` test case in the `java-demo` and `kotlin-demo` sample projects.
#### 3. Mock static methods of any class
@ -102,7 +102,7 @@ private BlackBox secretBox() {
}
```
For complete code examples, see the `should_able_to_mock_static_method()` test case in the `java-demo` and `kotlin-demo` sample projects.
For complete code examples, see the `should_mock_static_method()` test case in the `java-demo` and `kotlin-demo` sample projects.
#### 4. Mock `new` operation of any type
@ -123,7 +123,7 @@ private BlackBox createBlackBox(String text) {
> You can still use the `@MockMethod` annotation, and configure the `targetMethod` parameter value to `"<init>"`, and the rest is the same as above. The effect is the same as using the `@MockContructor` annotation
For complete code examples, see the `should_able_to_mock_new_object()` test case in the `java-demo` and `kotlin-demo` sample projects.
For complete code examples, see the `should_mock_new_object()` test case in the `java-demo` and `kotlin-demo` sample projects.
#### 5. Identify different invocation source in mock method
@ -157,7 +157,7 @@ private Data mockDemo() {
}
```
For complete code examples, see the `should_able_to_get_source_method_name()` and `should_able_to_get_test_case_name()` test cases in the `java-demo` and `kotlin-demo` sample projects.
For complete code examples, see the `should_get_source_method_name()` and `should_get_test_case_name()` test cases in the `java-demo` and `kotlin-demo` sample projects.
#### 6. Verify the sequence and parameters of the mock method being invoked

View File

@ -59,9 +59,9 @@ class DemoTest {
```text
[DIAGNOSE] Handling test class com/alibaba/testable/demo/basic/DemoMockTest
[VERBOSE] Test case "should_able_to_mock_new_object"
[VERBOSE] Test case "should_mock_new_object"
... ...
[VERBOSE] Test case "should_able_to_set_mock_context"
[VERBOSE] Test case "should_set_mock_context"
[DIAGNOSE] Found 6 test cases
[DIAGNOSE] Handling mock class com/alibaba/testable/demo/basic/DemoMockTest$Mock
[VERBOSE] Mock constructor "createBlackBox" as "com.alibaba.demo.basic.model.BlackBox(java.lang.String)"

View File

@ -19,7 +19,7 @@ public class DemoMockTest {
}
@Test
void should_able_to_mock_member_method() throws Exception {
void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world");
}
@ -39,7 +39,7 @@ public class DemoMockTest {
} // 增加此行
@Test
void should_able_to_mock_member_method() throws Exception {
void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world");
}

View File

@ -67,7 +67,7 @@ private String substring(String self, int i, int j) {
}
```
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_common_method()`测试用例。(由于Kotlin对String类型进行了魔改故Kotlin示例中将被测方法在`BlackBox`类里加了一层封装)
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_mock_common_method()`测试用例。(由于Kotlin对String类型进行了魔改故Kotlin示例中将被测方法在`BlackBox`类里加了一层封装)
#### 2. 覆写被测类自身的成员方法
@ -87,7 +87,7 @@ private String innerFunc(String text) {
同样的,上述示例中的方法如需访问发起调用的原始被测对象,也可不使用`targetClass`参数而是在定义Mock方法时在方法参数列表首位加一个类型为`DemoMock`的参数(名字随意)。
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_member_method()`测试用例。
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_mock_member_method()`测试用例。
#### 3. 覆写任意类的静态方法
@ -104,7 +104,7 @@ private BlackBox secretBox() {
对于静态方法的Mock通常不使用方法参数列表的首位加参数来表示目标类型。但这种方法也依然适用只是实际传入的第一个参数值将始终是`null`。
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_static_method()`测试用例。
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_mock_static_method()`测试用例。
#### 4. 覆写任意类的new操作
@ -123,7 +123,7 @@ private BlackBox createBlackBox(String text) {
}
```
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_new_object()`测试用例。
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_mock_new_object()`测试用例。
#### 5. 在Mock方法中区分调用来源
@ -157,7 +157,7 @@ private Data mockDemo() {
}
```
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_get_source_method_name()`和`should_able_to_get_test_case_name()`测试用例。
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_get_source_method_name()`和`should_get_test_case_name()`测试用例。
#### 6. 验证Mock方法被调用的顺序和参数

View File

@ -7,12 +7,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
class ClassUtilTest {
@Test
void should_able_to_convert_class_name() {
void should_convert_class_name() {
assertEquals("Ljava/lang/String;", ClassUtil.toByteCodeClassName("java.lang.String"));
}
@Test
void should_able_to_fit_companion_class_name() {
void should_fit_companion_class_name() {
assertEquals("com/intellij/rt/debugger/agent/CaptureAgent$ParamKeyProvider",
ClassUtil.fitCompanionClassName("com/intellij/rt/debugger/agent/CaptureAgent$ParamKeyProvider"));
assertEquals("com/alibaba/testable/demo/BlackBox",

View File

@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.*;
class CollectionUtilTest {
@Test
void should_able_to_check_collection_contains_any_element() {
void should_check_collection_contains_any_element() {
assertTrue(CollectionUtil.containsAny(
CollectionUtil.listOf("a", "b"), CollectionUtil.listOf("b", "c")
));

View File

@ -8,7 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
class MethodUtilTest {
@Test
void should_able_to_get_parameter_count() {
void should_get_parameter_count() {
assertEquals(0, MethodUtil.getParameterTypes("()V").size());
assertEquals(1, MethodUtil.getParameterTypes("(Ljava/lang/String;)V").size());
assertEquals(6, MethodUtil.getParameterTypes("(Ljava/lang/String;IDLjava/lang/String;ZLjava/net/URL;)V").size());
@ -17,13 +17,13 @@ class MethodUtilTest {
}
@Test
void should_able_to_extract_parameter() {
void should_extract_parameter() {
assertEquals("", MethodUtil.extractParameters("()I"));
assertEquals("Ljava/lang/String;", MethodUtil.extractParameters("(Ljava/lang/String;)I"));
}
@Test
void should_able_to_get_return_type() {
void should_get_return_type() {
assertEquals("V", MethodUtil.getReturnType("(Ljava/lang/String;)V"));
assertEquals("I", MethodUtil.getReturnType("(Ljava/lang/String;)I"));
assertEquals("[I", MethodUtil.getReturnType("(Ljava/lang/String;)[I"));
@ -32,14 +32,14 @@ class MethodUtilTest {
}
@Test
void should_able_to_get_first_parameter() {
void should_get_first_parameter() {
assertEquals("Ljava/lang/String;", MethodUtil.getFirstParameter("(Ljava/lang/String;Ljava/lang/Object;I)V"));
assertEquals("Ljava/lang/String;", MethodUtil.getFirstParameter("(Ljava/lang/String;)V"));
assertEquals("", MethodUtil.getFirstParameter("()V"));
}
@Test
void should_able_to_convert_bytecode_parameters() {
void should_convert_bytecode_parameters() {
assertEquals("", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", ""));
assertEquals("void", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "V"));
assertEquals("int, long", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "IJ"));

View File

@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.*;
class StringUtilTest {
@Test
void should_able_to_repeat_text() {
void should_repeat_text() {
assertEquals("", StringUtil.repeat("abc", 0));
assertEquals("abc", StringUtil.repeat("abc", 1));
assertEquals("abcabcabc", StringUtil.repeat("abc", 3));

View File

@ -15,6 +15,8 @@ public class PrivateAccessor {
private static final String KOTLIN_COMPANION_FIELD = "Companion";
private PrivateAccessor() {}
/**
* 读取任意类的私有字段
* @param ref 目标对象

View File

@ -0,0 +1,26 @@
package com.alibaba.testable.core.exception;
import java.lang.reflect.InvocationTargetException;
/**
* @author flin
*/
public class ClassConstructionException extends RuntimeException {
public ClassConstructionException(String message) {
super(message);
}
public ClassConstructionException(String message, Throwable cause) {
super(message, cause);
}
private static Throwable getRootCause(Throwable cause) {
if (cause instanceof InvocationTargetException) {
return ((InvocationTargetException)cause).getTargetException();
} else {
return cause;
}
}
}

View File

@ -8,6 +8,7 @@ import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static com.alibaba.testable.core.constant.ConstPool.SLASH;
@ -27,26 +28,28 @@ public class OmniAccessor {
private static final String BRACKET_START = "[";
private static final String BRACKET_END = "]";
private OmniAccessor() {}
/**
* 获取第一个符合搜索路径的成员
* @param target 目标对象
*
* @param target 目标对象
* @param queryPath 搜索路径
* @param clazz 目标成员类型
* @return 返回目标成员若不存在则返回null
*/
public static <T> T getFirst(Object target, String queryPath, Class<T> clazz) {
List<T> values = get(target, queryPath, clazz);
return values.isEmpty() ? null : values.get(0);
public static <T> T getFirst(Object target, String queryPath) {
T[] values = get(target, queryPath);
return values.length == 0 ? null : values[0];
}
/**
* 获取所有符合搜索路径的成员
* @param target 目标对象
*
* @param target 目标对象
* @param queryPath 搜索路径
* @param clazz 目标成员类型
* @return 返回所有匹配的成员
*/
public static <T> List<T> get(Object target, String queryPath, Class<T> clazz) {
public static <T> T[] get(Object target, String queryPath) {
List<T> values = new ArrayList<T>();
for (String memberPath : MEMBER_INDEXES.getOrElse(target.getClass(), generateMemberIndex(target.getClass()))) {
if (memberPath.matches(toPattern(queryPath))) {
@ -62,14 +65,15 @@ public class OmniAccessor {
}
}
}
return values;
return (T[])values.toArray();
}
/**
* 为符合搜索路径的成员赋值
* @param target 目标对象
*
* @param target 目标对象
* @param queryPath 搜索路径
* @param value 新的值
* @param value 新的值
* @return 实际影响的成员个数
*/
public static int set(Object target, String queryPath, Object value) {
@ -97,6 +101,9 @@ public class OmniAccessor {
}
private static List<String> generateMemberIndex(String basePath, Class<?> clazz) {
if (clazz.isEnum()) {
return Collections.emptyList();
}
List<Field> fields = TypeUtil.getAllFields(clazz);
List<String> paths = new ArrayList<String>();
for (Field f : fields) {
@ -155,12 +162,11 @@ public class OmniAccessor {
private static Object getByPath(Object target, String memberPath, String queryPath)
throws NoSuchFieldException, IllegalAccessException {
String[] memberSegments = memberPath.split(SLASH);
String[] querySegments = queryPath.split(SLASH);
String[] querySegments = calculateFullQueryPath(queryPath.split(SLASH), memberSegments);
Object obj = target;
String name;
int nth;
Field field;
assert memberSegments.length == querySegments.length;
for (int i = 0; i < memberSegments.length; i++) {
name = memberSegments[i].substring(0, memberSegments[i].indexOf(BRACE_START));
nth = extraIndexFromQuery(querySegments[i]);
@ -176,6 +182,21 @@ public class OmniAccessor {
return obj;
}
private static String[] calculateFullQueryPath(String[] querySegments, String[] memberSegments) {
assert memberSegments.length >= querySegments.length;
;
if (memberSegments.length > querySegments.length) {
String[] fullQuerySegments = new String[memberSegments.length];
for (int i = 0; i < querySegments.length; i++) {
fullQuerySegments[i] = "";
}
System.arraycopy(querySegments, 0, fullQuerySegments, querySegments.length,
memberSegments.length - querySegments.length);
return fullQuerySegments;
}
return querySegments;
}
private static void setByPathSegment(Object target, String memberSegment, String querySegment, Object value)
throws IllegalAccessException {
String name = memberSegment.substring(0, memberSegment.indexOf(BRACE_START));

View File

@ -1,5 +1,6 @@
package com.alibaba.testable.core.tool;
import com.alibaba.testable.core.exception.ClassConstructionException;
import com.alibaba.testable.core.model.Null;
import com.alibaba.testable.core.util.TypeUtil;
@ -10,6 +11,8 @@ import java.lang.reflect.*;
*/
public class OmniConstructor {
private OmniConstructor() {}
public static <T> T newInstance(Class<T> clazz) {
try {
if (clazz.isPrimitive()) {
@ -25,15 +28,15 @@ public class OmniConstructor {
}
return newObject(clazz);
} catch (NoSuchMethodException e) {
return null;
throw new ClassConstructionException("Failed to find constructor", e);
} catch (IllegalAccessException e) {
return null;
} catch (InstantiationException e) {
return null;
throw new ClassConstructionException("Failed to access constructor", e);
} catch (InvocationTargetException e) {
return null;
throw new ClassConstructionException("Failed to invoke constructor", e);
} catch (InstantiationException e) {
throw new ClassConstructionException("Failed to complete construction", e);
} catch (ClassCastException e) {
return null;
throw new ClassConstructionException("Unexpected type", e);
}
}
@ -88,15 +91,6 @@ public class OmniConstructor {
return null;
}
private static boolean isJavaAgentEnabled(Class<?> clazz) {
try {
clazz.getDeclaredConstructor(Null.class);
} catch (NoSuchMethodException e) {
return false;
}
return true;
}
private static Object newInstance(Constructor<?> constructor)
throws InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?>[] types = constructor.getParameterTypes();

View File

@ -33,24 +33,24 @@ class OmniAccessorTest {
@Test
void should_match_pattern() {
assertTrue("abc{Abc}".matches("abc\\{[^}]+\\}"));
assertTrue("abc{Abc}".matches("[^{]+\\{Abc\\}"));
assertTrue("abc{Abc[]}/xyz{Xyz[]}".matches("[^{]+\\{Abc\\[\\]\\}/[^{]+\\{Xyz\\[\\]\\}"));
assertTrue("abc{Abc}/xyz{Xyz}/demo{Demo}".matches("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}"));
assertTrue("/abc{Abc}".matches(".*/abc\\{[^}]+\\}"));
assertTrue("/abc{Abc}".matches(".*/[^{]+\\{Abc\\}"));
assertTrue("/abc{Abc[]}/xyz{Xyz[]}".matches(".*/[^{]+\\{Abc\\[\\]\\}/[^{]+\\{Xyz\\[\\]\\}"));
assertTrue("/abc{Abc}/xyz{Xyz}/demo{Demo}".matches(".*/abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}"));
}
@Test
void should_to_pattern() {
assertEquals("", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", ""));
assertEquals("abc\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc"));
assertEquals("[^{]+\\{Abc\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc}"));
assertEquals("abc\\{[^}]+\\}/xyz\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/xyz"));
assertEquals("[^{]+\\{Abc\\}/xyz\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc}/xyz"));
assertEquals("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}"));
assertEquals("[^{]+\\{Abc\\}/[^{]+\\{Xyz\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc}/{Xyz}"));
assertEquals("[^{]+\\{Abc\\[\\]\\}/[^{]+\\{Xyz\\[\\]\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc[]}/{Xyz[]}"));
assertEquals("abc\\{[^}]+\\}/[^{]+\\{Xyz\\[\\]\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc[1]/{Xyz[]}[2]"));
assertEquals("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}/demo"));
assertEquals(".*/", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", ""));
assertEquals(".*/abc\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc"));
assertEquals(".*/[^{]+\\{Abc\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc}"));
assertEquals(".*/abc\\{[^}]+\\}/xyz\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/xyz"));
assertEquals(".*/[^{]+\\{Abc\\}/xyz\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc}/xyz"));
assertEquals(".*/abc\\{[^}]+\\}/[^{]+\\{Xyz\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}"));
assertEquals(".*/[^{]+\\{Abc\\}/[^{]+\\{Xyz\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc}/{Xyz}"));
assertEquals(".*/[^{]+\\{Abc\\[\\]\\}/[^{]+\\{Xyz\\[\\]\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc[]}/{Xyz[]}"));
assertEquals(".*/abc\\{[^}]+\\}/[^{]+\\{Xyz\\[\\]\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc[1]/{Xyz[]}[2]"));
assertEquals(".*/abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}/demo"));
}
@Test
@ -71,21 +71,33 @@ class OmniAccessorTest {
assertEquals("xyz", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "/abc/def/xyz"));
}
@Test
void should_get_full_query_segments() {
String[] querySegments = new String[] { "c", "d" };
String[] memberSegments = new String[] { "a{A}", "b{B}", "c{C}", "d{D}" };
String[] fullQuerySegments = PrivateAccessor.invokeStatic(OmniAccessor.class, "calculateFullQueryPath", querySegments, memberSegments);
assertEquals(4, fullQuerySegments.length);
assertEquals("", fullQuerySegments[0]);
assertEquals("", fullQuerySegments[1]);
assertEquals("c", fullQuerySegments[2]);
assertEquals("d", fullQuerySegments[3]);
}
@Test
void should_get_by_path() {
DemoParent parent = prepareParentObject();
Object obj = PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "getByPath", parent, "c{DemoChild}/gc{DemoGrandChild}", "c/gc");
Object obj = PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "getByPath", parent, "/c{DemoChild}/gc{DemoGrandChild}", "c/gc");
assertTrue(obj instanceof DemoGrandChild);
assertEquals(0, ((DemoGrandChild)obj).get());
PrivateAccessor.set(parent.c, "gcs", new DemoGrandChild[] { new DemoGrandChild(4), new DemoGrandChild(6) });
obj = PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "getByPath", parent, "c{DemoChild}/gcs{DemoGrandChild[]}", "c/gcs");
obj = PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "getByPath", parent, "/c{DemoChild}/gcs{DemoGrandChild[]}", "c/gcs");
assertTrue(obj instanceof DemoGrandChild[]);
assertEquals(2, ((DemoGrandChild[])obj).length);
obj = PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "getByPath", parent, "c{DemoChild}/gcs{DemoGrandChild[]}", "c/gcs[1]");
obj = PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "getByPath", parent, "/c{DemoChild}/gcs{DemoGrandChild[]}", "c/gcs[1]");
assertTrue(obj instanceof DemoGrandChild);
assertEquals(6, ((DemoGrandChild)obj).get());
parent.cs = new DemoChild[] { null, prepareChildObject() };
obj = PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "getByPath", parent, "cs{DemoChild[]}/gcs{DemoGrandChild[]}/i{int}", "c[1]/gcs[1]/i");
obj = PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "getByPath", parent, "/cs{DemoChild[]}/gcs{DemoGrandChild[]}/i{int}", "c[1]/gcs[1]/i");
assertEquals(3, obj);
}

View File

@ -8,7 +8,7 @@ import static org.junit.jupiter.api.Assertions.*;
class MockAssociationUtilTest {
@Test
void should_able_to_associate_by_inner_mock_class() {
void should_associate_by_inner_mock_class() {
assertTrue((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByInnerMockClass",
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoTest$Mock"));
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByInnerMockClass",
@ -16,7 +16,7 @@ class MockAssociationUtilTest {
}
@Test
void should_able_to_associate_by_outer_mock_class() {
void should_associate_by_outer_mock_class() {
assertTrue((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByOuterMockClass",
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoMock"));
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByOuterMockClass",

View File

@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.*;
class StringUtilTest {
@Test
void should_able_to_join_string() {
void should_join_string() {
List<String> list = new ArrayList<String>(4);
list.add("a");
list.add("b");