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; package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.BlackBox; import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.demo.basic.model.Box; import com.alibaba.demo.basic.model.mock.Box;
import com.alibaba.demo.basic.model.Color; import com.alibaba.demo.basic.model.mock.Color;
import com.alibaba.demo.basic.model.BlackBox;
import com.alibaba.demo.basic.model.Box;
import com.alibaba.demo.basic.model.Color;
/** /**
* 演示父类变量引用子类对象时的Mock场景 * 演示父类变量引用子类对象时的Mock场景

View File

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

View File

@ -1,7 +1,6 @@
package com.alibaba.demo.basic; package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.BlackBox; import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.demo.basic.model.BlackBox;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; 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 { 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 { 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 { 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; package com.alibaba.demo.basic;
import com.alibaba.demo.basic.model.BlackBox; import com.alibaba.demo.basic.model.mock.BlackBox;
import com.alibaba.demo.basic.model.Box; import com.alibaba.demo.basic.model.mock.Box;
import com.alibaba.demo.basic.model.Color; import com.alibaba.demo.basic.model.mock.Color;
import com.alibaba.testable.core.annotation.MockMethod; 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 org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify; import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
@ -54,42 +50,42 @@ class DemoInheritTest {
} }
@Test @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(); BlackBox box = (BlackBox)demoInherit.putIntoBox();
verify("put_into_box").withTimes(1); verify("put_into_box").withTimes(1);
assertEquals("put_data_into_box", box.get()); assertEquals("put_data_into_box", box.get());
} }
@Test @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(); BlackBox box = demoInherit.putIntoBlackBox();
verify("put_into_blackbox").withTimes(1); verify("put_into_blackbox").withTimes(1);
assertEquals("put_data_into_blackbox", box.get()); assertEquals("put_data_into_blackbox", box.get());
} }
@Test @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(); String content = demoInherit.getFromBox();
verify("get_from_box").withTimes(1); verify("get_from_box").withTimes(1);
assertEquals("get_from_box", content); assertEquals("get_from_box", content);
} }
@Test @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(); String content = demoInherit.getFromBlackBox();
verify("get_from_blackbox").withTimes(1); verify("get_from_blackbox").withTimes(1);
assertEquals("get_from_blackbox", content); assertEquals("get_from_blackbox", content);
} }
@Test @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(); String color = demoInherit.getColorViaColor();
verify("get_color_from_color").withTimes(1); verify("get_color_from_color").withTimes(1);
assertEquals("color_from_color", color); assertEquals("color_from_color", color);
} }
@Test @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(); String color = demoInherit.getColorViaBox();
verify("get_color_from_blackbox").withTimes(1); verify("get_color_from_blackbox").withTimes(1);
assertEquals("color_from_blackbox", color); assertEquals("color_from_blackbox", color);

View File

@ -19,7 +19,7 @@ class DemoInnerClassTest {
} }
@Test @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(); DemoInnerClass demo = new DemoInnerClass();
assertEquals("MockedCall", demo.callInnerDemo()); assertEquals("MockedCall", demo.callInnerDemo());
assertEquals("MockedCall", demo.callAnonymousInner()); assertEquals("MockedCall", demo.callAnonymousInner());

View File

@ -1,10 +1,8 @@
package com.alibaba.demo.basic; 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.annotation.MockMethod;
import com.alibaba.testable.core.error.VerifyFailedError; 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 org.junit.jupiter.api.Test;
import static com.alibaba.testable.core.matcher.InvokeMatcher.*; import static com.alibaba.testable.core.matcher.InvokeMatcher.*;

View File

@ -1,9 +1,8 @@
package com.alibaba.demo.basic; 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.MockConstructor;
import com.alibaba.testable.core.annotation.MockMethod; 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.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -73,20 +72,20 @@ class DemoMockTest {
} }
@Test @Test
void should_able_to_mock_new_object() { void should_mock_new_object() {
assertEquals("mock_something", demoMock.newFunc()); assertEquals("mock_something", demoMock.newFunc());
verify("createBlackBox").with("something"); verify("createBlackBox").with("something");
} }
@Test @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")); assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"));
verify("innerFunc").with("hello"); verify("innerFunc").with("hello");
verify("staticFunc").with(); verify("staticFunc").with();
} }
@Test @Test
void should_able_to_mock_common_method() { void should_mock_common_method() {
assertEquals("trim_string__sub_string__false", demoMock.commonFunc()); assertEquals("trim_string__sub_string__false", demoMock.commonFunc());
verify("trim").withTimes(1); verify("trim").withTimes(1);
verify("sub").withTimes(1); verify("sub").withTimes(1);
@ -94,13 +93,13 @@ class DemoMockTest {
} }
@Test @Test
void should_able_to_mock_static_method() { void should_mock_static_method() {
Assertions.assertEquals("not_secret_box", demoMock.getBox().get()); Assertions.assertEquals("not_secret_box", demoMock.getBox().get());
verify("secretBox").withTimes(1); verify("secretBox").withTimes(1);
} }
@Test @Test
void should_able_to_get_source_method_name() throws Exception { void should_get_source_method_name() throws Exception {
// synchronous // synchronous
assertEquals("mock_one_mock_others", demoMock.callerOne() + "_" + demoMock.callerTwo()); assertEquals("mock_one_mock_others", demoMock.callerOne() + "_" + demoMock.callerTwo());
// asynchronous // asynchronous
@ -110,7 +109,7 @@ class DemoMockTest {
} }
@Test @Test
void should_able_to_set_mock_context() throws Exception { void should_set_mock_context() throws Exception {
MOCK_CONTEXT.put("case", "special_case"); MOCK_CONTEXT.put("case", "special_case");
// synchronous // synchronous
assertEquals("mock_special", demoMock.callerOne()); 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(); private DemoPrivateAccess demoPrivateAccess = new DemoPrivateAccess();
@Test @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"); }}; List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
assertEquals("member", demoPrivateAccess.privateFunc()); assertEquals("member", demoPrivateAccess.privateFunc());
assertEquals("member", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc")); assertEquals("member", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc"));
@ -30,7 +30,7 @@ class DemoPrivateAccessTest {
} }
@Test @Test
void should_able_to_access_private_field() { void should_access_private_field() {
demoPrivateAccess.count = 2; demoPrivateAccess.count = 2;
assertEquals(Integer.valueOf(2), demoPrivateAccess.count); assertEquals(Integer.valueOf(2), demoPrivateAccess.count);
@ -39,7 +39,7 @@ class DemoPrivateAccessTest {
} }
@Test @Test
void should_able_to_access_private_static_method() { void should_access_private_static_method() {
assertEquals("static", DemoPrivateAccess.privateStaticFunc()); assertEquals("static", DemoPrivateAccess.privateStaticFunc());
assertEquals("static", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFunc")); assertEquals("static", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFunc"));
assertEquals("hello + 1", DemoPrivateAccess.privateStaticFuncWithArgs("hello", 1)); assertEquals("hello + 1", DemoPrivateAccess.privateStaticFuncWithArgs("hello", 1));
@ -47,7 +47,7 @@ class DemoPrivateAccessTest {
} }
@Test @Test
void should_able_to_access_private_static_field() { void should_access_private_static_field() {
DemoPrivateAccess.staticCount = 2; DemoPrivateAccess.staticCount = 2;
assertEquals(Integer.valueOf(2), DemoPrivateAccess.staticCount); assertEquals(Integer.valueOf(2), DemoPrivateAccess.staticCount);
@ -56,7 +56,7 @@ class DemoPrivateAccessTest {
} }
@Test @Test
void should_able_to_update_final_field() { void should_update_final_field() {
demoPrivateAccess.pi = 4.13; demoPrivateAccess.pi = 4.13;
assertEquals(Double.valueOf(4.13), demoPrivateAccess.pi); assertEquals(Double.valueOf(4.13), demoPrivateAccess.pi);
@ -65,7 +65,7 @@ class DemoPrivateAccessTest {
} }
@Test @Test
void should_able_to_use_null_parameter() { void should_use_null_parameter() {
demoPrivateAccess.pi = null; demoPrivateAccess.pi = null;
assertNull(demoPrivateAccess.pi); assertNull(demoPrivateAccess.pi);
assertEquals("null + 1", DemoPrivateAccess.privateStaticFuncWithArgs(null, 1)); assertEquals("null + 1", DemoPrivateAccess.privateStaticFuncWithArgs(null, 1));

View File

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

View File

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

View File

@ -49,42 +49,42 @@ internal class DemoInheritTest {
} }
@Test @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 val box = demoInherit.putIntoBox() as BlackBox
InvokeVerifier.verify("put_into_box").withTimes(1) InvokeVerifier.verify("put_into_box").withTimes(1)
Assertions.assertEquals("put_data_into_box", box.get()) Assertions.assertEquals("put_data_into_box", box.get())
} }
@Test @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() val box = demoInherit.putIntoBlackBox()
InvokeVerifier.verify("put_into_blackbox").withTimes(1) InvokeVerifier.verify("put_into_blackbox").withTimes(1)
Assertions.assertEquals("put_data_into_blackbox", box.get()) Assertions.assertEquals("put_data_into_blackbox", box.get())
} }
@Test @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 val content = demoInherit.fromBox
InvokeVerifier.verify("get_from_box").withTimes(1) InvokeVerifier.verify("get_from_box").withTimes(1)
Assertions.assertEquals("get_from_box", content) Assertions.assertEquals("get_from_box", content)
} }
@Test @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 val content = demoInherit.fromBlackBox
InvokeVerifier.verify("get_from_blackbox").withTimes(1) InvokeVerifier.verify("get_from_blackbox").withTimes(1)
Assertions.assertEquals("get_from_blackbox", content) Assertions.assertEquals("get_from_blackbox", content)
} }
@Test @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 val color = demoInherit.colorViaColor
InvokeVerifier.verify("get_color_from_color").withTimes(1) InvokeVerifier.verify("get_color_from_color").withTimes(1)
Assertions.assertEquals("color_from_color", color) Assertions.assertEquals("color_from_color", color)
} }
@Test @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 val color = demoInherit.colorViaBox
InvokeVerifier.verify("get_color_from_blackbox").withTimes(1) InvokeVerifier.verify("get_color_from_blackbox").withTimes(1)
Assertions.assertEquals("color_from_blackbox", color) Assertions.assertEquals("color_from_blackbox", color)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ class OneToMultiSvcTest {
private val cSvc = CSvc() private val cSvc = CSvc()
@Test @Test
fun should_able_to_test_multi_class_together() { fun should_test_multi_class_together() {
Assertions.assertEquals("a_mock", aSvc.demo("test")) Assertions.assertEquals("a_mock", aSvc.demo("test"))
Assertions.assertEquals("b_mock", bSvc.demo("test")) Assertions.assertEquals("b_mock", bSvc.demo("test"))
Assertions.assertEquals("c_mock", cSvc.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 ```text
[DIAGNOSE] Handling test class com/alibaba/testable/demo/basic/DemoMockTest [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] Found 6 test cases
[DIAGNOSE] Handling mock class com/alibaba/testable/demo/basic/DemoMockTest$Mock [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)" [VERBOSE] Mock constructor "createBlackBox" as "com.alibaba.demo.basic.model.BlackBox(java.lang.String)"

View File

@ -19,7 +19,7 @@ public class DemoMockTest {
} }
@Test @Test
void should_able_to_mock_member_method() throws Exception { void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc()); assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world"); verify("innerFunc").with("world");
} }
@ -39,7 +39,7 @@ public class DemoMockTest {
} // Add this line } // Add this line
@Test @Test
void should_able_to_mock_member_method() throws Exception { void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc()); assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world"); 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 #### 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. 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 #### 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 #### 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 > 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 #### 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 #### 6. Verify the sequence and parameters of the mock method being invoked

View File

@ -59,9 +59,9 @@ class DemoTest {
```text ```text
[DIAGNOSE] Handling test class com/alibaba/testable/demo/basic/DemoMockTest [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] Found 6 test cases
[DIAGNOSE] Handling mock class com/alibaba/testable/demo/basic/DemoMockTest$Mock [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)" [VERBOSE] Mock constructor "createBlackBox" as "com.alibaba.demo.basic.model.BlackBox(java.lang.String)"

View File

@ -19,7 +19,7 @@ public class DemoMockTest {
} }
@Test @Test
void should_able_to_mock_member_method() throws Exception { void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc()); assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world"); verify("innerFunc").with("world");
} }
@ -39,7 +39,7 @@ public class DemoMockTest {
} // 增加此行 } // 增加此行
@Test @Test
void should_able_to_mock_member_method() throws Exception { void should_mock_member_method() throws Exception {
assertEquals("hello_world", demoMock.outerFunc()); assertEquals("hello_world", demoMock.outerFunc());
verify("innerFunc").with("world"); 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. 覆写被测类自身的成员方法 #### 2. 覆写被测类自身的成员方法
@ -87,7 +87,7 @@ private String innerFunc(String text) {
同样的,上述示例中的方法如需访问发起调用的原始被测对象,也可不使用`targetClass`参数而是在定义Mock方法时在方法参数列表首位加一个类型为`DemoMock`的参数(名字随意)。 同样的,上述示例中的方法如需访问发起调用的原始被测对象,也可不使用`targetClass`参数而是在定义Mock方法时在方法参数列表首位加一个类型为`DemoMock`的参数(名字随意)。
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_member_method()`测试用例。 完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_mock_member_method()`测试用例。
#### 3. 覆写任意类的静态方法 #### 3. 覆写任意类的静态方法
@ -104,7 +104,7 @@ private BlackBox secretBox() {
对于静态方法的Mock通常不使用方法参数列表的首位加参数来表示目标类型。但这种方法也依然适用只是实际传入的第一个参数值将始终是`null`。 对于静态方法的Mock通常不使用方法参数列表的首位加参数来表示目标类型。但这种方法也依然适用只是实际传入的第一个参数值将始终是`null`。
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_static_method()`测试用例。 完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_mock_static_method()`测试用例。
#### 4. 覆写任意类的new操作 #### 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方法中区分调用来源 #### 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方法被调用的顺序和参数 #### 6. 验证Mock方法被调用的顺序和参数

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -33,24 +33,24 @@ class OmniAccessorTest {
@Test @Test
void should_match_pattern() { void should_match_pattern() {
assertTrue("abc{Abc}".matches("abc\\{[^}]+\\}")); assertTrue("/abc{Abc}".matches(".*/abc\\{[^}]+\\}"));
assertTrue("abc{Abc}".matches("[^{]+\\{Abc\\}")); assertTrue("/abc{Abc}".matches(".*/[^{]+\\{Abc\\}"));
assertTrue("abc{Abc[]}/xyz{Xyz[]}".matches("[^{]+\\{Abc\\[\\]\\}/[^{]+\\{Xyz\\[\\]\\}")); assertTrue("/abc{Abc[]}/xyz{Xyz[]}".matches(".*/[^{]+\\{Abc\\[\\]\\}/[^{]+\\{Xyz\\[\\]\\}"));
assertTrue("abc{Abc}/xyz{Xyz}/demo{Demo}".matches("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}")); assertTrue("/abc{Abc}/xyz{Xyz}/demo{Demo}".matches(".*/abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}"));
} }
@Test @Test
void should_to_pattern() { void should_to_pattern() {
assertEquals("", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "")); 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\\}", 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/{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\\[\\]\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc[1]/{Xyz[]}[2]"));
assertEquals("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}/demo")); assertEquals(".*/abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}/demo"));
} }
@Test @Test
@ -71,21 +71,33 @@ class OmniAccessorTest {
assertEquals("xyz", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "/abc/def/xyz")); 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 @Test
void should_get_by_path() { void should_get_by_path() {
DemoParent parent = prepareParentObject(); 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); assertTrue(obj instanceof DemoGrandChild);
assertEquals(0, ((DemoGrandChild)obj).get()); assertEquals(0, ((DemoGrandChild)obj).get());
PrivateAccessor.set(parent.c, "gcs", new DemoGrandChild[] { new DemoGrandChild(4), new DemoGrandChild(6) }); 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[]); assertTrue(obj instanceof DemoGrandChild[]);
assertEquals(2, ((DemoGrandChild[])obj).length); 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); assertTrue(obj instanceof DemoGrandChild);
assertEquals(6, ((DemoGrandChild)obj).get()); assertEquals(6, ((DemoGrandChild)obj).get());
parent.cs = new DemoChild[] { null, prepareChildObject() }; 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); assertEquals(3, obj);
} }

View File

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

View File

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