mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-03-10 09:40:30 +08:00
use MockMethod and MockConstructor replace TestableMock annotation
This commit is contained in:
parent
857b5e2295
commit
328c8540a8
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
换种思路写Mock,让单元测试更简单。
|
换种思路写Mock,让单元测试更简单。
|
||||||
|
|
||||||
无需初始化,不挑测试框架,甭管要换的方法是被测类的私有方法、静态方法还是其他任何类的成员方法,也甭管要换的对象是怎么创建的。写好Mock方法,加个`@TestableMock`注解,一切统统搞定。
|
无需初始化,不挑测试框架,甭管要换的方法是被测类的私有方法、静态方法还是其他任何类的成员方法,也甭管要换的对象是怎么创建的。写好Mock方法,加个`@MockMethod`注解,一切统统搞定。
|
||||||
|
|
||||||
文档:https://alibaba.github.io/testable-mock/
|
文档:https://alibaba.github.io/testable-mock/
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package com.alibaba.testable.demo;
|
package com.alibaba.testable.demo;
|
||||||
|
|
||||||
import com.alibaba.testable.core.annotation.TestableMock;
|
import com.alibaba.testable.core.annotation.MockMethod;
|
||||||
import com.alibaba.testable.demo.model.BlackBox;
|
import com.alibaba.testable.demo.model.BlackBox;
|
||||||
import com.alibaba.testable.demo.model.Box;
|
import com.alibaba.testable.demo.model.Box;
|
||||||
import com.alibaba.testable.demo.model.Color;
|
import com.alibaba.testable.demo.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;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 演示父类变量引用子类对象时的Mock场景
|
* 演示父类变量引用子类对象时的Mock场景
|
||||||
@ -17,32 +17,32 @@ class DemoInheritTest {
|
|||||||
|
|
||||||
private DemoInherit demoInherit = new DemoInherit();
|
private DemoInherit demoInherit = new DemoInherit();
|
||||||
|
|
||||||
@TestableMock(targetMethod = "put")
|
@MockMethod(targetMethod = "put")
|
||||||
private void put_into_box(Box self, String something) {
|
private void put_into_box(Box self, String something) {
|
||||||
self.put("put_" + something + "_into_box");
|
self.put("put_" + something + "_into_box");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "put")
|
@MockMethod(targetMethod = "put")
|
||||||
private void put_into_blackbox(BlackBox self, String something) {
|
private void put_into_blackbox(BlackBox self, String something) {
|
||||||
self.put("put_" + something + "_into_blackbox");
|
self.put("put_" + something + "_into_blackbox");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "get")
|
@MockMethod(targetMethod = "get")
|
||||||
private String get_from_box(Box self) {
|
private String get_from_box(Box self) {
|
||||||
return "get_from_box";
|
return "get_from_box";
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "get")
|
@MockMethod(targetMethod = "get")
|
||||||
private String get_from_blackbox(BlackBox self) {
|
private String get_from_blackbox(BlackBox self) {
|
||||||
return "get_from_blackbox";
|
return "get_from_blackbox";
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "getColor")
|
@MockMethod(targetMethod = "getColor")
|
||||||
private String get_color_from_color(Color self) {
|
private String get_color_from_color(Color self) {
|
||||||
return "color_from_color";
|
return "color_from_color";
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "getColor")
|
@MockMethod(targetMethod = "getColor")
|
||||||
private String get_color_from_blackbox(BlackBox self) {
|
private String get_color_from_blackbox(BlackBox self) {
|
||||||
return "color_from_blackbox";
|
return "color_from_blackbox";
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.alibaba.testable.demo;
|
package com.alibaba.testable.demo;
|
||||||
|
|
||||||
import com.alibaba.testable.core.annotation.TestableMock;
|
import com.alibaba.testable.core.annotation.MockMethod;
|
||||||
import com.alibaba.testable.core.error.VerifyFailedError;
|
import com.alibaba.testable.core.error.VerifyFailedError;
|
||||||
import com.alibaba.testable.demo.model.BlackBox;
|
import com.alibaba.testable.demo.model.BlackBox;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -17,15 +17,16 @@ class DemoMatcherTest {
|
|||||||
|
|
||||||
private DemoMatcher demoMatcher = new DemoMatcher();
|
private DemoMatcher demoMatcher = new DemoMatcher();
|
||||||
|
|
||||||
@TestableMock(targetMethod = "methodToBeMocked")
|
@MockMethod(targetMethod = "methodToBeMocked")
|
||||||
private void methodWithoutArgument(DemoMatcher self) {}
|
private void methodWithoutArgument(DemoMatcher self) {}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "methodToBeMocked")
|
@MockMethod(targetMethod = "methodToBeMocked")
|
||||||
private void methodWithArguments(DemoMatcher self, Object a1, Object a2) {}
|
private void methodWithArguments(DemoMatcher self, Object a1, Object a2) {}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "methodToBeMocked")
|
@MockMethod(targetMethod = "methodToBeMocked")
|
||||||
private void methodWithArrayArgument(DemoMatcher self, Object[] a) {}
|
private void methodWithArrayArgument(DemoMatcher self, Object[] a) {}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_match_no_argument() {
|
void should_match_no_argument() {
|
||||||
demoMatcher.callMethodWithoutArgument();
|
demoMatcher.callMethodWithoutArgument();
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
package com.alibaba.testable.demo;
|
package com.alibaba.testable.demo;
|
||||||
|
|
||||||
import com.alibaba.testable.core.annotation.TestableMock;
|
import com.alibaba.testable.core.annotation.MockConstructor;
|
||||||
import com.alibaba.testable.core.tool.TestableConst;
|
import com.alibaba.testable.core.annotation.MockMethod;
|
||||||
import com.alibaba.testable.demo.model.BlackBox;
|
import com.alibaba.testable.demo.model.BlackBox;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||||
import static com.alibaba.testable.core.tool.TestableTool.*;
|
import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD;
|
||||||
|
import static com.alibaba.testable.core.tool.TestableTool.TEST_CASE;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,37 +20,37 @@ class DemoMockTest {
|
|||||||
|
|
||||||
private DemoMock demoMock = new DemoMock();
|
private DemoMock demoMock = new DemoMock();
|
||||||
|
|
||||||
@TestableMock(targetMethod = TestableConst.CONSTRUCTOR)
|
@MockConstructor
|
||||||
private BlackBox createBlackBox(String text) {
|
private BlackBox createBlackBox(String text) {
|
||||||
return new BlackBox("mock_" + text);
|
return new BlackBox("mock_" + text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private String innerFunc(DemoMock self, String text) {
|
private String innerFunc(DemoMock self, String text) {
|
||||||
return "mock_" + text;
|
return "mock_" + text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private String trim(String self) {
|
private String trim(String self) {
|
||||||
return "trim_string";
|
return "trim_string";
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "substring")
|
@MockMethod(targetMethod = "substring")
|
||||||
private String sub(String self, int i, int j) {
|
private String sub(String self, int i, int j) {
|
||||||
return "sub_string";
|
return "sub_string";
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private boolean startsWith(String self, String s) {
|
private boolean startsWith(String self, String s) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private BlackBox secretBox(BlackBox ignore) {
|
private BlackBox secretBox(BlackBox ignore) {
|
||||||
return new BlackBox("not_secret_box");
|
return new BlackBox("not_secret_box");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private String callFromDifferentMethod(DemoMock self) {
|
private String callFromDifferentMethod(DemoMock self) {
|
||||||
if (TEST_CASE.equals("should_able_to_get_test_case_name")) {
|
if (TEST_CASE.equals("should_able_to_get_test_case_name")) {
|
||||||
return "mock_special";
|
return "mock_special";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.alibaba.testable.demo;
|
package com.alibaba.testable.demo;
|
||||||
|
|
||||||
import com.alibaba.testable.core.annotation.TestableMock;
|
import com.alibaba.testable.core.annotation.MockConstructor;
|
||||||
import com.alibaba.testable.core.tool.TestableConst;
|
import com.alibaba.testable.core.annotation.MockMethod;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -19,24 +19,24 @@ class DemoTemplateTest {
|
|||||||
/* 第一种写法:使用泛型定义 */
|
/* 第一种写法:使用泛型定义 */
|
||||||
/* First solution: use generics type */
|
/* First solution: use generics type */
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private <T> List<T> getList(DemoTemplate self, T value) {
|
private <T> List<T> getList(DemoTemplate self, T value) {
|
||||||
return new ArrayList<T>() {{ add((T)(value.toString() + "_mock_list")); }};
|
return new ArrayList<T>() {{ add((T)(value.toString() + "_mock_list")); }};
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private <K, V> Map<K, V> getMap(DemoTemplate self, K key, V value) {
|
private <K, V> Map<K, V> getMap(DemoTemplate self, K key, V value) {
|
||||||
return new HashMap<K, V>() {{ put(key, (V)(value.toString() + "_mock_map")); }};
|
return new HashMap<K, V>() {{ put(key, (V)(value.toString() + "_mock_map")); }};
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = TestableConst.CONSTRUCTOR)
|
@MockConstructor
|
||||||
private <T> HashSet<T> newHashSet() {
|
private <T> HashSet<T> newHashSet() {
|
||||||
HashSet<T> set = new HashSet<>();
|
HashSet<T> set = new HashSet<>();
|
||||||
set.add((T)"insert_mock");
|
set.add((T)"insert_mock");
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private <E> boolean add(Set s, E e) {
|
private <E> boolean add(Set s, E e) {
|
||||||
s.add(e.toString() + "_mocked");
|
s.add(e.toString() + "_mocked");
|
||||||
return true;
|
return true;
|
||||||
@ -45,24 +45,24 @@ class DemoTemplateTest {
|
|||||||
/* 第二种写法:使用Object类型 */
|
/* 第二种写法:使用Object类型 */
|
||||||
/* Second solution: use object type */
|
/* Second solution: use object type */
|
||||||
|
|
||||||
//@TestableMock
|
//@MockMethod
|
||||||
//private List<Object> getList(DemoTemplate self, Object value) {
|
//private List<Object> getList(DemoTemplate self, Object value) {
|
||||||
// return new ArrayList<Object>() {{ add(value.toString() + "_mock_list"); }};
|
// return new ArrayList<Object>() {{ add(value.toString() + "_mock_list"); }};
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
//@TestableMock
|
//@MockMethod
|
||||||
//private Map<Object, Object> getMap(DemoTemplate self, Object key, Object value) {
|
//private Map<Object, Object> getMap(DemoTemplate self, Object key, Object value) {
|
||||||
// return new HashMap<Object, Object>() {{ put(key, value.toString() + "_mock_map"); }};
|
// return new HashMap<Object, Object>() {{ put(key, value.toString() + "_mock_map"); }};
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
//@TestableMock(targetMethod = TestableConst.CONSTRUCTOR)
|
//@MockConstructor
|
||||||
//private HashSet newHashSet() {
|
//private HashSet newHashSet() {
|
||||||
// HashSet<Object> set = new HashSet<>();
|
// HashSet<Object> set = new HashSet<>();
|
||||||
// set.add("insert_mock");
|
// set.add("insert_mock");
|
||||||
// return set;
|
// return set;
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
//@TestableMock
|
//@MockMethod
|
||||||
//private boolean add(Set s, Object e) {
|
//private boolean add(Set s, Object e) {
|
||||||
// s.add(e.toString() + "_mocked");
|
// s.add(e.toString() + "_mocked");
|
||||||
// return true;
|
// return true;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.alibaba.testable.demo
|
package com.alibaba.testable.demo
|
||||||
|
|
||||||
import com.alibaba.testable.core.annotation.TestableMock
|
import com.alibaba.testable.core.annotation.MockMethod
|
||||||
import com.alibaba.testable.core.matcher.InvokeVerifier
|
import com.alibaba.testable.core.matcher.InvokeVerifier
|
||||||
import com.alibaba.testable.demo.model.BlackBox
|
import com.alibaba.testable.demo.model.BlackBox
|
||||||
import com.alibaba.testable.demo.model.Box
|
import com.alibaba.testable.demo.model.Box
|
||||||
@ -14,37 +14,38 @@ import org.junit.jupiter.api.Test
|
|||||||
*/
|
*/
|
||||||
internal class DemoInheritTest {
|
internal class DemoInheritTest {
|
||||||
|
|
||||||
@TestableMock(targetMethod = "put")
|
private val demoInherit = DemoInherit()
|
||||||
|
|
||||||
|
@MockMethod(targetMethod = "put")
|
||||||
private fun put_into_box(self: Box, something: String) {
|
private fun put_into_box(self: Box, something: String) {
|
||||||
self.put("put_" + something + "_into_box")
|
self.put("put_" + something + "_into_box")
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "put")
|
@MockMethod(targetMethod = "put")
|
||||||
private fun put_into_blackbox(self: BlackBox, something: String) {
|
private fun put_into_blackbox(self: BlackBox, something: String) {
|
||||||
self.put("put_" + something + "_into_blackbox")
|
self.put("put_" + something + "_into_blackbox")
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "get")
|
@MockMethod(targetMethod = "get")
|
||||||
private fun get_from_box(self: Box): String {
|
private fun get_from_box(self: Box): String {
|
||||||
return "get_from_box"
|
return "get_from_box"
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "get")
|
@MockMethod(targetMethod = "get")
|
||||||
private fun get_from_blackbox(self: BlackBox): String {
|
private fun get_from_blackbox(self: BlackBox): String {
|
||||||
return "get_from_blackbox"
|
return "get_from_blackbox"
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "getColor")
|
@MockMethod(targetMethod = "getColor")
|
||||||
private fun get_color_from_color(self: Color): String {
|
private fun get_color_from_color(self: Color): String {
|
||||||
return "color_from_color"
|
return "color_from_color"
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "getColor")
|
@MockMethod(targetMethod = "getColor")
|
||||||
private fun get_color_from_blackbox(self: BlackBox): String {
|
private fun get_color_from_blackbox(self: BlackBox): String {
|
||||||
return "color_from_blackbox"
|
return "color_from_blackbox"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val demoInherit = DemoInherit()
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun should_able_to_mock_call_sub_object_method_by_parent_object() {
|
fun should_able_to_mock_call_sub_object_method_by_parent_object() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.alibaba.testable.demo
|
package com.alibaba.testable.demo
|
||||||
|
|
||||||
import com.alibaba.testable.core.annotation.TestableMock
|
import com.alibaba.testable.core.annotation.MockMethod
|
||||||
import com.alibaba.testable.core.error.VerifyFailedError
|
import com.alibaba.testable.core.error.VerifyFailedError
|
||||||
import com.alibaba.testable.core.matcher.InvokeMatcher
|
import com.alibaba.testable.core.matcher.InvokeMatcher
|
||||||
import com.alibaba.testable.core.matcher.InvokeVerifier
|
import com.alibaba.testable.core.matcher.InvokeVerifier
|
||||||
@ -14,19 +14,20 @@ import org.junit.jupiter.api.Test
|
|||||||
*/
|
*/
|
||||||
internal class DemoMatcherTest {
|
internal class DemoMatcherTest {
|
||||||
|
|
||||||
@TestableMock(targetMethod = "methodToBeMocked")
|
private val demoMatcher = DemoMatcher()
|
||||||
|
|
||||||
|
@MockMethod(targetMethod = "methodToBeMocked")
|
||||||
private fun methodWithoutArgument(self: DemoMatcher) {
|
private fun methodWithoutArgument(self: DemoMatcher) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "methodToBeMocked")
|
@MockMethod(targetMethod = "methodToBeMocked")
|
||||||
private fun methodWithArguments(self: DemoMatcher, a1: Any, a2: Any) {
|
private fun methodWithArguments(self: DemoMatcher, a1: Any, a2: Any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = "methodToBeMocked")
|
@MockMethod(targetMethod = "methodToBeMocked")
|
||||||
private fun methodWithArrayArgument(self: DemoMatcher, a: Array<Any>) {
|
private fun methodWithArrayArgument(self: DemoMatcher, a: Array<Any>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private val demoMatcher = DemoMatcher()
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun should_match_no_argument() {
|
fun should_match_no_argument() {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package com.alibaba.testable.demo
|
package com.alibaba.testable.demo
|
||||||
|
|
||||||
import com.alibaba.testable.core.annotation.TestableMock
|
import com.alibaba.testable.core.annotation.MockConstructor
|
||||||
|
import com.alibaba.testable.core.annotation.MockMethod
|
||||||
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
|
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
|
||||||
import com.alibaba.testable.core.tool.TestableConst
|
import com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD
|
||||||
import com.alibaba.testable.core.tool.TestableTool.*
|
import com.alibaba.testable.core.tool.TestableTool.TEST_CASE
|
||||||
import com.alibaba.testable.demo.model.BlackBox
|
import com.alibaba.testable.demo.model.BlackBox
|
||||||
import com.alibaba.testable.demo.model.ColorBox
|
import com.alibaba.testable.demo.model.ColorBox
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
@ -16,32 +17,34 @@ import java.util.concurrent.Executors
|
|||||||
*/
|
*/
|
||||||
internal class DemoMockTest {
|
internal class DemoMockTest {
|
||||||
|
|
||||||
@TestableMock(targetMethod = TestableConst.CONSTRUCTOR)
|
private val demoMock = DemoMock()
|
||||||
|
|
||||||
|
@MockConstructor
|
||||||
private fun createBlackBox(text: String) = BlackBox("mock_$text")
|
private fun createBlackBox(text: String) = BlackBox("mock_$text")
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private fun innerFunc(self: DemoMock, text: String) = "mock_$text"
|
private fun innerFunc(self: DemoMock, text: String) = "mock_$text"
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private fun trim(self: BlackBox) = "trim_string"
|
private fun trim(self: BlackBox) = "trim_string"
|
||||||
|
|
||||||
@TestableMock(targetMethod = "substring")
|
@MockMethod(targetMethod = "substring")
|
||||||
private fun sub(self: BlackBox, i: Int, j: Int) = "sub_string"
|
private fun sub(self: BlackBox, i: Int, j: Int) = "sub_string"
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private fun startsWith(self: BlackBox, s: String) = false
|
private fun startsWith(self: BlackBox, s: String) = false
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private fun secretBox(ignore: BlackBox): BlackBox {
|
private fun secretBox(ignore: BlackBox): BlackBox {
|
||||||
return BlackBox("not_secret_box")
|
return BlackBox("not_secret_box")
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private fun createBox(ignore: ColorBox, color: String, box: BlackBox): BlackBox {
|
private fun createBox(ignore: ColorBox, color: String, box: BlackBox): BlackBox {
|
||||||
return BlackBox("White_${box.get()}")
|
return BlackBox("White_${box.get()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private fun callFromDifferentMethod(self: DemoMock): String {
|
private fun callFromDifferentMethod(self: DemoMock): String {
|
||||||
return if (TEST_CASE == "should_able_to_get_test_case_name") {
|
return if (TEST_CASE == "should_able_to_get_test_case_name") {
|
||||||
"mock_special"
|
"mock_special"
|
||||||
@ -53,7 +56,6 @@ internal class DemoMockTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val demoMock = DemoMock()
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun should_able_to_mock_new_object() {
|
fun should_able_to_mock_new_object() {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.alibaba.testable.demo
|
package com.alibaba.testable.demo
|
||||||
|
|
||||||
|
import com.alibaba.testable.core.annotation.MockConstructor
|
||||||
|
import com.alibaba.testable.core.annotation.MockMethod
|
||||||
import com.alibaba.testable.core.annotation.MockWith
|
import com.alibaba.testable.core.annotation.MockWith
|
||||||
import com.alibaba.testable.core.annotation.TestableMock
|
|
||||||
import com.alibaba.testable.core.model.MockDiagnose
|
import com.alibaba.testable.core.model.MockDiagnose
|
||||||
import com.alibaba.testable.core.tool.TestableConst
|
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -17,29 +17,30 @@ internal class DemoTemplateTest {
|
|||||||
|
|
||||||
private val demoTemplate = DemoTemplate()
|
private val demoTemplate = DemoTemplate()
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private fun <T> getList(self: DemoTemplate, value: T): List<T> {
|
private fun <T> getList(self: DemoTemplate, value: T): List<T> {
|
||||||
return mutableListOf((value.toString() + "_mock_list") as T)
|
return mutableListOf((value.toString() + "_mock_list") as T)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private fun <K, V> getMap(self: DemoTemplate, key: K, value: V): Map<K, V> {
|
private fun <K, V> getMap(self: DemoTemplate, key: K, value: V): Map<K, V> {
|
||||||
return mutableMapOf(key to (value.toString() + "_mock_map") as V)
|
return mutableMapOf(key to (value.toString() + "_mock_map") as V)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = TestableConst.CONSTRUCTOR)
|
@MockConstructor
|
||||||
private fun newHashSet(): HashSet<*> {
|
private fun newHashSet(): HashSet<*> {
|
||||||
val set = HashSet<Any>()
|
val set = HashSet<Any>()
|
||||||
set.add("insert_mock")
|
set.add("insert_mock")
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private fun <E> add(s: MutableSet<E>, e: E): Boolean {
|
private fun <E> add(s: MutableSet<E>, e: E): Boolean {
|
||||||
s.add((e.toString() + "_mocked") as E)
|
s.add((e.toString() + "_mocked") as E)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun should_able_to_mock_single_template_method() {
|
fun should_able_to_mock_single_template_method() {
|
||||||
val res = demoTemplate.singleTemplateMethod()
|
val res = demoTemplate.singleTemplateMethod()
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package com.alibaba.testable.demo.util
|
package com.alibaba.testable.demo.util
|
||||||
|
|
||||||
import com.alibaba.testable.core.annotation.TestableMock
|
import com.alibaba.testable.core.annotation.MockMethod
|
||||||
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
|
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class PathUtilTest {
|
class PathUtilTest {
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
fun exists(f: File): Boolean {
|
fun exists(f: File): Boolean {
|
||||||
return when (f.absolutePath) {
|
return when (f.absolutePath) {
|
||||||
"/a/b" -> true
|
"/a/b" -> true
|
||||||
@ -16,7 +16,7 @@ class PathUtilTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
fun isDirectory(f: File): Boolean {
|
fun isDirectory(f: File): Boolean {
|
||||||
return when (f.absolutePath) {
|
return when (f.absolutePath) {
|
||||||
"/a/b/c" -> true
|
"/a/b/c" -> true
|
||||||
@ -24,12 +24,12 @@ class PathUtilTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
fun delete(f: File): Boolean {
|
fun delete(f: File): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@MockMethod
|
||||||
fun listFiles(f: File): Array<File>? {
|
fun listFiles(f: File): Array<File>? {
|
||||||
return when (f.absolutePath) {
|
return when (f.absolutePath) {
|
||||||
"/a/b" -> arrayOf(File("/a/b/c"), File("/a/b/d"))
|
"/a/b" -> arrayOf(File("/a/b/c"), File("/a/b/d"))
|
||||||
|
@ -77,7 +77,7 @@ class DemoTest {
|
|||||||
private Demo demo = new Demo();
|
private Demo demo = new Demo();
|
||||||
|
|
||||||
// 拦截`System.out.println`调用
|
// 拦截`System.out.println`调用
|
||||||
@TestableMock
|
@MockMethod
|
||||||
public void println(PrintStream ps, String msg) {
|
public void println(PrintStream ps, String msg) {
|
||||||
// 执行原调用
|
// 执行原调用
|
||||||
ps.println(msg);
|
ps.println(msg);
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
#### 1. 覆写任意类的方法调用
|
#### 1. 覆写任意类的方法调用
|
||||||
|
|
||||||
在测试类里定义一个有`@TestableMock`注解的普通方法,使它与需覆写的方法名称、参数、返回值类型完全一致,然后在其参数列表首位再增加一个类型为该方法原本所属对象类型的参数。
|
在测试类里定义一个有`@MockMethod`注解的普通方法,使它与需覆写的方法名称、参数、返回值类型完全一致,然后在其参数列表首位再增加一个类型为该方法原本所属对象类型的参数。
|
||||||
|
|
||||||
此时被测类中所有对该需覆写方法的调用,将在单元测试运行时,将自动被替换为对上述自定义Mock方法的调用。
|
此时被测类中所有对该需覆写方法的调用,将在单元测试运行时,将自动被替换为对上述自定义Mock方法的调用。
|
||||||
|
|
||||||
**注意**:当遇到待覆写方法有重名时,可以将需覆写的方法名写到`@TestableMock`注解的`targetMethod`参数里,这样Mock方法自身就可以随意命名了。
|
**注意**:当遇到待覆写方法有重名时,可以将需覆写的方法名写到`@MockMethod`注解的`targetMethod`参数里,这样Mock方法自身就可以随意命名了。
|
||||||
|
|
||||||
例如,被测类中有一处`"anything".substring(1, 2)`调用,我们希望在运行测试的时候将它换成一个固定字符串,则只需在测试类定义如下方法:
|
例如,被测类中有一处`"anything".substring(1, 2)`调用,我们希望在运行测试的时候将它换成一个固定字符串,则只需在测试类定义如下方法:
|
||||||
|
|
||||||
@ -20,7 +20,7 @@
|
|||||||
// 调用此方法的对象`"anything"`类型为`String`
|
// 调用此方法的对象`"anything"`类型为`String`
|
||||||
// 则Mock方法签名在其参数列表首位增加一个类型为`String`的参数(名字随意)
|
// 则Mock方法签名在其参数列表首位增加一个类型为`String`的参数(名字随意)
|
||||||
// 此参数可用于获得当时的实际调用者的值和上下文
|
// 此参数可用于获得当时的实际调用者的值和上下文
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private String substring(String self, int i, int j) {
|
private String substring(String self, int i, int j) {
|
||||||
return "sub_string";
|
return "sub_string";
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ private String substring(String self, int i, int j) {
|
|||||||
```java
|
```java
|
||||||
// 使用`targetMethod`指定需Mock的方法名
|
// 使用`targetMethod`指定需Mock的方法名
|
||||||
// 此方法本身现在可以随意命名,但方法参数依然需要遵循相同的匹配规则
|
// 此方法本身现在可以随意命名,但方法参数依然需要遵循相同的匹配规则
|
||||||
@TestableMock(targetMethod = "substring")
|
@MockMethod(targetMethod = "substring")
|
||||||
private String use_any_mock_method_name(String self, int i, int j) {
|
private String use_any_mock_method_name(String self, int i, int j) {
|
||||||
return "sub_string";
|
return "sub_string";
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ private String use_any_mock_method_name(String self, int i, int j) {
|
|||||||
```java
|
```java
|
||||||
// 被测类型是`DemoMock`
|
// 被测类型是`DemoMock`
|
||||||
// 因此在定义Mock方法时,在目标方法参数首位加一个类型为`DemoMock`的参数(名字随意)
|
// 因此在定义Mock方法时,在目标方法参数首位加一个类型为`DemoMock`的参数(名字随意)
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private String innerFunc(DemoMock self, String text) {
|
private String innerFunc(DemoMock self, String text) {
|
||||||
return "mock_" + text;
|
return "mock_" + text;
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ private String innerFunc(DemoMock self, String text) {
|
|||||||
// 目标静态方法定义在`BlackBox`类型中
|
// 目标静态方法定义在`BlackBox`类型中
|
||||||
// 在定义Mock方法时,在目标方法参数首位加一个类型为`BlackBox`的参数(名字随意)
|
// 在定义Mock方法时,在目标方法参数首位加一个类型为`BlackBox`的参数(名字随意)
|
||||||
// 此参数仅用于标识目标类型,实际传入值将始终为`null`
|
// 此参数仅用于标识目标类型,实际传入值将始终为`null`
|
||||||
@TestableMock
|
@MockMethod
|
||||||
private BlackBox secretBox(BlackBox ignore) {
|
private BlackBox secretBox(BlackBox ignore) {
|
||||||
return new BlackBox("not_secret_box");
|
return new BlackBox("not_secret_box");
|
||||||
}
|
}
|
||||||
@ -78,7 +78,7 @@ private BlackBox secretBox(BlackBox ignore) {
|
|||||||
|
|
||||||
#### 4. 覆写任意类的new操作
|
#### 4. 覆写任意类的new操作
|
||||||
|
|
||||||
在测试类里定义一个有`@TestableMock`注解的普通方法,将注解的`targetMethod`参数写为"<init>",然后使该方法与要被创建类型的构造函数参数、返回值类型完全一致,方法名称随意。
|
在测试类里定义一个有`@MockContructor`注解的普通方法,使该方法返回值类型为要被创建的对象类型,且与要Mock的构造函数参数完全一致,方法名称随意。
|
||||||
|
|
||||||
此时被测类中所有用`new`创建指定类的操作(并使用了与Mock方法参数一致的构造函数)将被替换为对该自定义方法的调用。
|
此时被测类中所有用`new`创建指定类的操作(并使用了与Mock方法参数一致的构造函数)将被替换为对该自定义方法的调用。
|
||||||
|
|
||||||
@ -86,14 +86,15 @@ private BlackBox secretBox(BlackBox ignore) {
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
// 要覆写的构造函数签名为`BlackBox(String)`
|
// 要覆写的构造函数签名为`BlackBox(String)`
|
||||||
// 无需在Mock方法参数列表增加额外参数,由于使用了`targetMethod`参数,Mock方法的名称随意起
|
// 无需在Mock方法参数列表增加额外参数,Mock方法的名称随意起
|
||||||
// 此处的`TestableConst.CONSTRUCTOR`为`TestableMock`提供的辅助常量,值为"<init>"
|
@MockContructor
|
||||||
@TestableMock(targetMethod = TestableConst.CONSTRUCTOR)
|
|
||||||
private BlackBox createBlackBox(String text) {
|
private BlackBox createBlackBox(String text) {
|
||||||
return new BlackBox("mock_" + text);
|
return new BlackBox("mock_" + text);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> 也可以使用`@MockMethod`注解,并将`targetMethod`参数写为"<init>",其余同上,效果与`@MockContructor`注解相同
|
||||||
|
|
||||||
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_new_object()`测试用例。
|
完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_new_object()`测试用例。
|
||||||
|
|
||||||
#### 5. 识别当前测试用例和调用来源
|
#### 5. 识别当前测试用例和调用来源
|
||||||
|
@ -14,5 +14,12 @@ public class ConstPool {
|
|||||||
public static final String FIELD_TARGET_METHOD = "targetMethod";
|
public static final String FIELD_TARGET_METHOD = "targetMethod";
|
||||||
|
|
||||||
public static final String MOCK_WITH = "com.alibaba.testable.core.annotation.MockWith";
|
public static final String MOCK_WITH = "com.alibaba.testable.core.annotation.MockWith";
|
||||||
public static final String TESTABLE_MOCK = "com.alibaba.testable.core.annotation.TestableMock";
|
public static final String MOCK_METHOD = "com.alibaba.testable.core.annotation.MockMethod";
|
||||||
|
public static final String MOCK_CONSTRUCTOR = "com.alibaba.testable.core.annotation.MockConstructor";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the constructor method
|
||||||
|
*/
|
||||||
|
public static final String CONSTRUCTOR = "<init>";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import com.alibaba.testable.agent.constant.ConstPool;
|
|||||||
import com.alibaba.testable.agent.model.MethodInfo;
|
import com.alibaba.testable.agent.model.MethodInfo;
|
||||||
import com.alibaba.testable.agent.util.BytecodeUtil;
|
import com.alibaba.testable.agent.util.BytecodeUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
import com.alibaba.testable.core.tool.TestableConst;
|
|
||||||
import com.alibaba.testable.core.util.LogUtil;
|
import com.alibaba.testable.core.util.LogUtil;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
@ -40,7 +39,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
Set<MethodInfo> memberInjectMethods = new HashSet<MethodInfo>();
|
Set<MethodInfo> memberInjectMethods = new HashSet<MethodInfo>();
|
||||||
Set<MethodInfo> newOperatorInjectMethods = new HashSet<MethodInfo>();
|
Set<MethodInfo> newOperatorInjectMethods = new HashSet<MethodInfo>();
|
||||||
for (MethodInfo mi : injectMethods) {
|
for (MethodInfo mi : injectMethods) {
|
||||||
if (mi.getName().equals(TestableConst.CONSTRUCTOR)) {
|
if (mi.getName().equals(ConstPool.CONSTRUCTOR)) {
|
||||||
newOperatorInjectMethods.add(mi);
|
newOperatorInjectMethods.add(mi);
|
||||||
} else {
|
} else {
|
||||||
memberInjectMethods.add(mi);
|
memberInjectMethods.add(mi);
|
||||||
@ -69,7 +68,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
node.owner, node.getOpcode(), rangeStart, i);
|
node.owner, node.getOpcode(), rangeStart, i);
|
||||||
i = rangeStart;
|
i = rangeStart;
|
||||||
}
|
}
|
||||||
} else if (TestableConst.CONSTRUCTOR.equals(node.name)) {
|
} else if (ConstPool.CONSTRUCTOR.equals(node.name)) {
|
||||||
// it's a new operation
|
// it's a new operation
|
||||||
String newOperatorInjectMethodName = getNewOperatorInjectMethodName(newOperatorInjectMethods, node);
|
String newOperatorInjectMethodName = getNewOperatorInjectMethodName(newOperatorInjectMethods, node);
|
||||||
if (newOperatorInjectMethodName != null) {
|
if (newOperatorInjectMethodName != null) {
|
||||||
@ -127,7 +126,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
case Opcodes.INVOKEVIRTUAL:
|
case Opcodes.INVOKEVIRTUAL:
|
||||||
case Opcodes.INVOKEINTERFACE:
|
case Opcodes.INVOKEINTERFACE:
|
||||||
stackLevel += stackEffectOfInvocation(instructions[i]) + 1;
|
stackLevel += stackEffectOfInvocation(instructions[i]) + 1;
|
||||||
if (((MethodInsnNode)instructions[i]).name.equals(TestableConst.CONSTRUCTOR)) {
|
if (((MethodInsnNode)instructions[i]).name.equals(ConstPool.CONSTRUCTOR)) {
|
||||||
// constructor must be INVOKESPECIAL and implicitly pop 1 more stack
|
// constructor must be INVOKESPECIAL and implicitly pop 1 more stack
|
||||||
stackLevel++;
|
stackLevel++;
|
||||||
}
|
}
|
||||||
@ -202,7 +201,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
mn.instructions.remove(instructions[end - 1]);
|
mn.instructions.remove(instructions[end - 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// method with @TestableMock will be modified as public access, so INVOKEVIRTUAL is used
|
// method with @MockMethod will be modified as public access, so INVOKEVIRTUAL is used
|
||||||
mn.instructions.insertBefore(instructions[end], new MethodInsnNode(INVOKEVIRTUAL, testClassName,
|
mn.instructions.insertBefore(instructions[end], new MethodInsnNode(INVOKEVIRTUAL, testClassName,
|
||||||
substitutionMethod, addFirstParameter(method.desc, ClassUtil.fitCompanionClassName(ownerClass)), false));
|
substitutionMethod, addFirstParameter(method.desc, ClassUtil.fitCompanionClassName(ownerClass)), false));
|
||||||
mn.instructions.remove(instructions[end]);
|
mn.instructions.remove(instructions[end]);
|
||||||
|
@ -4,12 +4,13 @@ import com.alibaba.testable.agent.constant.ConstPool;
|
|||||||
import com.alibaba.testable.agent.tool.ImmutablePair;
|
import com.alibaba.testable.agent.tool.ImmutablePair;
|
||||||
import com.alibaba.testable.agent.util.AnnotationUtil;
|
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
import com.alibaba.testable.core.tool.TestableConst;
|
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author flin
|
* @author flin
|
||||||
*/
|
*/
|
||||||
@ -56,7 +57,8 @@ public class TestClassHandler extends BaseClassHandler {
|
|||||||
for (AnnotationNode n : mn.visibleAnnotations) {
|
for (AnnotationNode n : mn.visibleAnnotations) {
|
||||||
visibleAnnotationNames.add(n.desc);
|
visibleAnnotationNames.add(n.desc);
|
||||||
}
|
}
|
||||||
if (visibleAnnotationNames.contains(ClassUtil.toByteCodeClassName(ConstPool.TESTABLE_MOCK))) {
|
if (visibleAnnotationNames.contains(ClassUtil.toByteCodeClassName(ConstPool.MOCK_METHOD)) ||
|
||||||
|
visibleAnnotationNames.contains(ClassUtil.toByteCodeClassName(ConstPool.MOCK_CONSTRUCTOR))) {
|
||||||
mn.access &= ~ACC_PRIVATE;
|
mn.access &= ~ACC_PRIVATE;
|
||||||
mn.access &= ~ACC_PROTECTED;
|
mn.access &= ~ACC_PROTECTED;
|
||||||
mn.access |= ACC_PUBLIC;
|
mn.access |= ACC_PUBLIC;
|
||||||
@ -136,9 +138,12 @@ public class TestClassHandler extends BaseClassHandler {
|
|||||||
|
|
||||||
private boolean isMockForConstructor(MethodNode mn) {
|
private boolean isMockForConstructor(MethodNode mn) {
|
||||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
|
if (toDotSeparateFullClassName(an.desc).equals(ConstPool.MOCK_CONSTRUCTOR)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
String method = AnnotationUtil.getAnnotationParameter
|
String method = AnnotationUtil.getAnnotationParameter
|
||||||
(an, ConstPool.FIELD_TARGET_METHOD, null, String.class);
|
(an, ConstPool.FIELD_TARGET_METHOD, null, String.class);
|
||||||
if (TestableConst.CONSTRUCTOR.equals(method)) {
|
if (ConstPool.CONSTRUCTOR.equals(method)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import com.alibaba.testable.agent.model.MethodInfo;
|
|||||||
import com.alibaba.testable.agent.tool.ComparableWeakRef;
|
import com.alibaba.testable.agent.tool.ComparableWeakRef;
|
||||||
import com.alibaba.testable.agent.util.AnnotationUtil;
|
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
import com.alibaba.testable.core.tool.TestableConst;
|
|
||||||
import com.alibaba.testable.core.util.LogUtil;
|
import com.alibaba.testable.core.util.LogUtil;
|
||||||
import com.alibaba.testable.core.model.MockDiagnose;
|
import com.alibaba.testable.core.model.MockDiagnose;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
@ -117,21 +116,33 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
if (toDotSeparateFullClassName(an.desc).equals(ConstPool.TESTABLE_MOCK)) {
|
String fullClassName = toDotSeparateFullClassName(an.desc);
|
||||||
String targetClass = ClassUtil.toSlashSeparateFullClassName(methodDescPair.left);
|
if (fullClassName.equals(ConstPool.MOCK_CONSTRUCTOR)) {
|
||||||
|
addMockConstructor(cn, methodInfos, mn);
|
||||||
|
} else if (fullClassName.equals(ConstPool.MOCK_METHOD)) {
|
||||||
String targetMethod = AnnotationUtil.getAnnotationParameter(
|
String targetMethod = AnnotationUtil.getAnnotationParameter(
|
||||||
an, ConstPool.FIELD_TARGET_METHOD, mn.name, String.class);
|
an, ConstPool.FIELD_TARGET_METHOD, mn.name, String.class);
|
||||||
if (targetMethod.equals(TestableConst.CONSTRUCTOR)) {
|
if (targetMethod.equals(ConstPool.CONSTRUCTOR)) {
|
||||||
String sourceClassName = ClassUtil.getSourceClassName(cn.name);
|
addMockConstructor(cn, methodInfos, mn);
|
||||||
methodInfos.add(new MethodInfo(sourceClassName, targetMethod, mn.name, mn.desc));
|
|
||||||
} else {
|
} else {
|
||||||
methodInfos.add(new MethodInfo(targetClass, targetMethod, mn.name, methodDescPair.right));
|
addMockMethod(methodInfos, mn, methodDescPair, targetMethod);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addMockMethod(List<MethodInfo> methodInfos, MethodNode mn,
|
||||||
|
ImmutablePair<String, String> methodDescPair, String targetMethod) {
|
||||||
|
String targetClass = ClassUtil.toSlashSeparateFullClassName(methodDescPair.left);
|
||||||
|
methodInfos.add(new MethodInfo(targetClass, targetMethod, mn.name, methodDescPair.right));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMockConstructor(ClassNode cn, List<MethodInfo> methodInfos, MethodNode mn) {
|
||||||
|
String sourceClassName = ClassUtil.getSourceClassName(cn.name);
|
||||||
|
methodInfos.add(new MethodInfo(sourceClassName, ConstPool.CONSTRUCTOR, mn.name, mn.desc));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether any method in specified class has specified annotation
|
* Check whether any method in specified class has specified annotation
|
||||||
* @param className class that need to explore
|
* @param className class that need to explore
|
||||||
@ -158,7 +169,9 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
for (MethodNode mn : cn.methods) {
|
for (MethodNode mn : cn.methods) {
|
||||||
if (mn.visibleAnnotations != null) {
|
if (mn.visibleAnnotations != null) {
|
||||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
if (toDotSeparateFullClassName(an.desc).equals(ConstPool.TESTABLE_MOCK)) {
|
String fullClassName = toDotSeparateFullClassName(an.desc);
|
||||||
|
if (fullClassName.equals(ConstPool.MOCK_METHOD) ||
|
||||||
|
fullClassName.equals(ConstPool.MOCK_CONSTRUCTOR)) {
|
||||||
loadedClass.put(new ComparableWeakRef<String>(className), CachedMockParameter.exist());
|
loadedClass.put(new ComparableWeakRef<String>(className), CachedMockParameter.exist());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.alibaba.testable.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark method as mock constructor
|
||||||
|
*
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
public @interface MockConstructor {
|
||||||
|
}
|
@ -3,14 +3,14 @@ package com.alibaba.testable.core.annotation;
|
|||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use marked method to replace the ones in source class
|
* Mark method as mock method
|
||||||
*
|
*
|
||||||
* @author flin
|
* @author flin
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface TestableMock {
|
public @interface MockMethod {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mock specified method instead of method with same name
|
* mock specified method instead of method with same name
|
@ -1,13 +0,0 @@
|
|||||||
package com.alibaba.testable.core.tool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author flin
|
|
||||||
*/
|
|
||||||
public class TestableConst {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of the constructor method
|
|
||||||
*/
|
|
||||||
public static final String CONSTRUCTOR = "<init>";
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user