mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-24 11:20:26 +08:00
update doc about generics type method and add kotlin demo
This commit is contained in:
parent
857cbac81c
commit
757becc08b
@ -15,6 +15,8 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
*/
|
*/
|
||||||
class DemoInheritTest {
|
class DemoInheritTest {
|
||||||
|
|
||||||
|
private DemoInherit demoInherit = new DemoInherit();
|
||||||
|
|
||||||
@TestableMock(targetMethod = "put")
|
@TestableMock(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");
|
||||||
@ -45,7 +47,6 @@ class DemoInheritTest {
|
|||||||
return "color_from_blackbox";
|
return "color_from_blackbox";
|
||||||
}
|
}
|
||||||
|
|
||||||
private DemoInherit demoInherit = new DemoInherit();
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_able_to_mock_call_sub_object_method_by_parent_object() {
|
void should_able_to_mock_call_sub_object_method_by_parent_object() {
|
||||||
|
@ -17,6 +17,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||||||
*/
|
*/
|
||||||
class DemoMockTest {
|
class DemoMockTest {
|
||||||
|
|
||||||
|
private DemoMock demoMock = new DemoMock();
|
||||||
|
|
||||||
@TestableMock(targetMethod = CONSTRUCTOR)
|
@TestableMock(targetMethod = CONSTRUCTOR)
|
||||||
private BlackBox createBlackBox(String text) {
|
private BlackBox createBlackBox(String text) {
|
||||||
return new BlackBox("mock_" + text);
|
return new BlackBox("mock_" + text);
|
||||||
@ -58,7 +60,6 @@ class DemoMockTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DemoMock demoMock = new DemoMock();
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_able_to_mock_new_object() {
|
void should_able_to_mock_new_object() {
|
||||||
|
@ -16,6 +16,9 @@ class DemoTemplateTest {
|
|||||||
|
|
||||||
private DemoTemplate demoTemplate = new DemoTemplate();
|
private DemoTemplate demoTemplate = new DemoTemplate();
|
||||||
|
|
||||||
|
/* 第一种写法:使用泛型定义 */
|
||||||
|
/* First solution: use generics type */
|
||||||
|
|
||||||
@TestableMock
|
@TestableMock
|
||||||
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")); }};
|
||||||
@ -27,9 +30,9 @@ class DemoTemplateTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock(targetMethod = TestableTool.CONSTRUCTOR)
|
@TestableMock(targetMethod = TestableTool.CONSTRUCTOR)
|
||||||
public HashSet newHashSet() {
|
private <T> HashSet<T> newHashSet() {
|
||||||
HashSet<Object> set = new HashSet<>();
|
HashSet<T> set = new HashSet<>();
|
||||||
set.add("insert_mock");
|
set.add((T)"insert_mock");
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,6 +42,33 @@ class DemoTemplateTest {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 第二种写法:使用Object类型 */
|
||||||
|
/* Second solution: use object type */
|
||||||
|
|
||||||
|
//@TestableMock
|
||||||
|
//private List<Object> getList(DemoTemplate self, Object value) {
|
||||||
|
// return new ArrayList<Object>() {{ add(value.toString() + "_mock_list"); }};
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//@TestableMock
|
||||||
|
//private Map<Object, Object> getMap(DemoTemplate self, Object key, Object value) {
|
||||||
|
// return new HashMap<Object, Object>() {{ put(key, value.toString() + "_mock_map"); }};
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//@TestableMock(targetMethod = TestableTool.CONSTRUCTOR)
|
||||||
|
//private HashSet newHashSet() {
|
||||||
|
// HashSet<Object> set = new HashSet<>();
|
||||||
|
// set.add("insert_mock");
|
||||||
|
// return set;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//@TestableMock
|
||||||
|
//private boolean add(Set s, Object e) {
|
||||||
|
// s.add(e.toString() + "_mocked");
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_able_to_mock_single_template_method() {
|
void should_able_to_mock_single_template_method() {
|
||||||
String res = demoTemplate.singleTemplateMethod();
|
String res = demoTemplate.singleTemplateMethod();
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.alibaba.testable.demo
|
||||||
|
|
||||||
|
import java.util.ArrayList
|
||||||
|
import java.util.HashMap
|
||||||
|
import java.util.HashSet
|
||||||
|
|
||||||
|
class DemoTemplate {
|
||||||
|
private fun <T> getList(value: T): List<T> {
|
||||||
|
val l: MutableList<T> = ArrayList()
|
||||||
|
l.add(value)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <K, V> getMap(key: K, value: V): Map<K, V> {
|
||||||
|
val m: MutableMap<K, V> = HashMap()
|
||||||
|
m[key] = value
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
fun singleTemplateMethod(): String {
|
||||||
|
val list = getList("demo")
|
||||||
|
return list[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doubleTemplateMethod(): String? {
|
||||||
|
val map = getMap("hello", "testable")
|
||||||
|
return map["hello"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newTemplateMethod(): Set<*> {
|
||||||
|
val set: MutableSet<String> = HashSet()
|
||||||
|
set.add("world")
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,10 @@ import com.alibaba.testable.demo.model.Color
|
|||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 演示父类变量引用子类对象时的Mock场景
|
||||||
|
* Demonstrate scenario of mocking method from sub-type object referred by parent-type variable
|
||||||
|
*/
|
||||||
internal class DemoInheritTest {
|
internal class DemoInheritTest {
|
||||||
|
|
||||||
@TestableMock(targetMethod = "put")
|
@TestableMock(targetMethod = "put")
|
||||||
|
@ -8,7 +8,10 @@ import com.alibaba.testable.demo.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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 演示Mock方法调用校验器
|
||||||
|
* Demonstrate mock method invocation verifier
|
||||||
|
*/
|
||||||
internal class DemoMatcherTest {
|
internal class DemoMatcherTest {
|
||||||
|
|
||||||
@TestableMock(targetMethod = "methodToBeMocked")
|
@TestableMock(targetMethod = "methodToBeMocked")
|
||||||
|
@ -9,6 +9,10 @@ import org.junit.jupiter.api.Assertions.assertEquals
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 演示基本的Mock功能
|
||||||
|
* Demonstrate basic mock functionality
|
||||||
|
*/
|
||||||
internal class DemoMockTest {
|
internal class DemoMockTest {
|
||||||
|
|
||||||
@TestableMock(targetMethod = CONSTRUCTOR)
|
@TestableMock(targetMethod = CONSTRUCTOR)
|
||||||
|
@ -4,7 +4,10 @@ import com.alibaba.testable.core.accessor.PrivateAccessor
|
|||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 演示私有成员访问功能
|
||||||
|
* Demonstrate private member access functionality
|
||||||
|
*/
|
||||||
internal class DemoPrivateAccessTest {
|
internal class DemoPrivateAccessTest {
|
||||||
|
|
||||||
private val demoPrivateAccess = DemoPrivateAccess()
|
private val demoPrivateAccess = DemoPrivateAccess()
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
package com.alibaba.testable.demo
|
||||||
|
|
||||||
|
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.tool.TestableTool
|
||||||
|
import org.junit.jupiter.api.Assertions
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 演示模板方法可以被Mock
|
||||||
|
* Demonstrate template method can be mocked
|
||||||
|
*/
|
||||||
|
@MockWith(diagnose = MockDiagnose.ENABLE)
|
||||||
|
internal class DemoTemplateTest {
|
||||||
|
|
||||||
|
private val demoTemplate = DemoTemplate()
|
||||||
|
|
||||||
|
@TestableMock
|
||||||
|
private fun <T> getList(self: DemoTemplate, value: T): List<T> {
|
||||||
|
return mutableListOf((value.toString() + "_mock_list") as T)
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestableMock
|
||||||
|
private fun <K, V> getMap(self: DemoTemplate, key: K, value: V): Map<K, V> {
|
||||||
|
return mutableMapOf(key to (value.toString() + "_mock_map") as V)
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestableMock(targetMethod = TestableTool.CONSTRUCTOR)
|
||||||
|
private fun newHashSet(): HashSet<*> {
|
||||||
|
val set = HashSet<Any>()
|
||||||
|
set.add("insert_mock")
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestableMock
|
||||||
|
private fun <E> add(s: MutableSet<E>, e: E): Boolean {
|
||||||
|
s.add((e.toString() + "_mocked") as E)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun should_able_to_mock_single_template_method() {
|
||||||
|
val res = demoTemplate.singleTemplateMethod()
|
||||||
|
Assertions.assertEquals("demo_mock_list", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun should_able_to_mock_double_template_method() {
|
||||||
|
val res = demoTemplate.doubleTemplateMethod()
|
||||||
|
Assertions.assertEquals("testable_mock_map", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun should_able_to_mock_new_template_method() {
|
||||||
|
val res = demoTemplate.newTemplateMethod()
|
||||||
|
Assertions.assertEquals(2, res.size)
|
||||||
|
val iterator = res.stream().iterator()
|
||||||
|
Assertions.assertEquals("insert_mock", iterator.next())
|
||||||
|
Assertions.assertEquals("world_mocked", iterator.next())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,23 +5,37 @@
|
|||||||
|
|
||||||
直接创建被测类对象,然后利用`TestableMock`访问私有成员的能力直接给这些字段赋值即可。
|
直接创建被测类对象,然后利用`TestableMock`访问私有成员的能力直接给这些字段赋值即可。
|
||||||
|
|
||||||
#### 2. 父类变量指向子类对象时,如何实现Mock方法?
|
#### 2. `TestableMock`是否能够与其他Mock工具一起使用?
|
||||||
|
|
||||||
|
`TestableMock`可与其他基于动态代理机制的Mock工具安全的共同使用,譬如`Mockito`、`EasyMock`、`MockRunner`等皆属此范畴。
|
||||||
|
|
||||||
|
对于其他会修改类加载器或被测类字节码的Mock工具,譬如`PowerMock`和`JMockit`,尚无案例证明会与`TestableMock`发生冲突,但从原理来说二者可能存在不兼容风险,请谨慎使用。
|
||||||
|
|
||||||
|
#### 3. 父类变量指向子类对象时,如何实现Mock方法?
|
||||||
|
|
||||||
在代码中,经常会有使用<u>接口变量或父类变量</u>指向子类实例,调用父类或子类方法的情况。
|
在代码中,经常会有使用<u>接口变量或父类变量</u>指向子类实例,调用父类或子类方法的情况。
|
||||||
|
|
||||||
这时候遵循一个原则,Mock方法的首个参数类型**始终与发起调用的变量类型一致**。
|
这时候遵循一个原则,Mock方法的首个参数类型**始终与发起调用的变量类型一致**。
|
||||||
|
|
||||||
因此,不论被调用方法来自父类还是子类,也不论子类是否覆写该方法,Mock方法的首个参数类型都应该使用变量自身的接口或父类类型。
|
因此,不论实际被调用方法来自父类还是子类,也不论子类是否覆写该方法。若变量为父类型(或接口类型),则Mock方法的首个参数类型都应该使用相同的父类(或接口)类型。
|
||||||
|
|
||||||
参见Java和Kotlin示例中`DemoInheritTest`测试类的用例。
|
参见Java和Kotlin示例中`DemoInheritTest`测试类的用例。
|
||||||
|
|
||||||
#### 3. 在Kotlin项目对`String`类中的方法进行Mock不生效?
|
#### 4. 如何Mock对于泛型方法(模板方法)?
|
||||||
|
|
||||||
|
与普通方法的Mock方法相同,直接在Mock方法上使用相同的泛型参数即可。
|
||||||
|
|
||||||
|
参见Java和Kotlin示例中`DemoTemplateTest`测试类的用例。
|
||||||
|
|
||||||
|
不过,由于JVM存在泛型擦除机制,对于Java项目也可以直接使用`Object`类型替代泛型参数,见Java版`DemoTemplateTest`测试类中被注释掉的"第二种写法"示例。
|
||||||
|
|
||||||
|
#### 5. 在Kotlin项目对`String`类中的方法进行Mock不生效?
|
||||||
|
|
||||||
Kotlin语言中的`String`类型实际上是`kotlin.String`,而非`java.lang.String`。但在构建生成自字节码的时候又会被替换为Java的`java.lang.String`类,因此无论将Mock目标写为`kotlin.String`或`java.lang.String`均无法正常匹配到原始的被调用方法。
|
Kotlin语言中的`String`类型实际上是`kotlin.String`,而非`java.lang.String`。但在构建生成自字节码的时候又会被替换为Java的`java.lang.String`类,因此无论将Mock目标写为`kotlin.String`或`java.lang.String`均无法正常匹配到原始的被调用方法。
|
||||||
|
|
||||||
实际场景中需要对`String`类中的方法进行Mock的场景很少,`TestableMock`暂未对这种情况做特别处理。
|
实际场景中需要对`String`类中的方法进行Mock的场景很少,`TestableMock`暂未对这种情况做特别处理。
|
||||||
|
|
||||||
#### 4. `TestableMock`能否用于Android项目的测试?
|
#### 6. `TestableMock`能否用于Android项目的测试?
|
||||||
|
|
||||||
结合[Roboelectric](https://github.com/robolectric/robolectric)测试框架可使用。
|
结合[Roboelectric](https://github.com/robolectric/robolectric)测试框架可使用。
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user