mirror of
https://github.com/alibaba/testable-mock.git
synced 2026-08-20 02:03:30 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2d456da97 | ||
|
|
28500558fc | ||
|
|
990f407ebe | ||
|
|
f6206ee294 | ||
|
|
8cfba333ff | ||
|
|
397fc3d2fa | ||
|
|
b7a076dfaf | ||
|
|
aee2ec2767 | ||
|
|
682d822249 | ||
|
|
6cf3f993ca | ||
|
|
0cce5a6ec0 |
@@ -2,7 +2,7 @@
|
||||
|
||||
换种思路写Mock,让单元测试更简单。
|
||||
|
||||
无需初始化,不挑服务框架,甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法,也甭管要换的对象是怎么创建的。写好Mock定义,加个`@MockMethod`注解,一切统统搞定。
|
||||
无需初始化,不挑服务框架,甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法,也甭管要换的对象是怎么创建的。写好Mock定义,加个`@MockInvoke`注解,一切统统搞定。
|
||||
|
||||
- 文档:https://alibaba.github.io/testable-mock/
|
||||
- 国内文档镜像:http://freyrlin.gitee.io/testable-mock/
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Write mock faster, make unit testing easier.
|
||||
|
||||
Any test framework, no initialization, no matter private method, static method, construction method, or any other method of any class, and no matter how the object created.
|
||||
Write a mock method, add an `@MockMethod` annotation, everything is done.
|
||||
Write a mock method, add an `@MockInvoke` annotation, everything is done.
|
||||
|
||||
Usage Document: https://alibaba.github.io/testable-mock/#/en-us/
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ dependencies {
|
||||
testImplementation 'androidx.test:runner:1.4.0-alpha05'
|
||||
testImplementation 'junit:junit:4.+'
|
||||
testImplementation 'org.robolectric:robolectric:4.5.1'
|
||||
testImplementation 'com.alibaba.testable:testable-all:0.6.9'
|
||||
testImplementation 'com.alibaba.testable:testable-all:0.6.10'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.alibaba.testable.demo;
|
||||
|
||||
import com.alibaba.testable.demo.model.BlackBox;
|
||||
import com.alibaba.testable.core.annotation.MockConstructor;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockNew;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static com.alibaba.testable.core.tool.TestableTool.MOCK_CONTEXT;
|
||||
import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -23,42 +23,42 @@ public class DemoBasicTest {
|
||||
private DemoBasic demoBasic = new DemoBasic();
|
||||
|
||||
public static class Mock {
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
private BlackBox createBlackBox(String text) {
|
||||
return new BlackBox("mock_" + text);
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoBasic.class)
|
||||
@MockInvoke(targetClass = DemoBasic.class)
|
||||
private String innerFunc(String text) {
|
||||
return "mock_" + text;
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoBasic.class)
|
||||
@MockInvoke(targetClass = DemoBasic.class)
|
||||
private String staticFunc() {
|
||||
return "_MOCK_TAIL";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = String.class)
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private String trim() {
|
||||
return "trim_string";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = String.class, targetMethod = "substring")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "substring")
|
||||
private String sub(int i, int j) {
|
||||
return "sub_string";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = String.class)
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private boolean startsWith(String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = BlackBox.class)
|
||||
@MockInvoke(targetClass = BlackBox.class)
|
||||
private BlackBox secretBox() {
|
||||
return new BlackBox("not_secret_box");
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoBasic.class)
|
||||
@MockInvoke(targetClass = DemoBasic.class)
|
||||
private String callFromDifferentMethod() {
|
||||
if ("special_case".equals(MOCK_CONTEXT.get("case"))) {
|
||||
return "mock_special";
|
||||
@@ -75,28 +75,28 @@ public class DemoBasicTest {
|
||||
@Test
|
||||
public void should_mock_new_object() {
|
||||
assertEquals("mock_something", demoBasic.newFunc());
|
||||
verify("createBlackBox").with("something");
|
||||
verifyInvoked("createBlackBox").with("something");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_mock_member_method() throws Exception {
|
||||
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoBasic.outerFunc("hello"));
|
||||
verify("innerFunc").with("hello");
|
||||
verify("staticFunc").with();
|
||||
verifyInvoked("innerFunc").with("hello");
|
||||
verifyInvoked("staticFunc").with();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_mock_common_method() {
|
||||
assertEquals("trim_string__sub_string__false", demoBasic.commonFunc());
|
||||
verify("trim").withTimes(1);
|
||||
verify("sub").withTimes(1);
|
||||
verify("startsWith").withTimes(1);
|
||||
verifyInvoked("trim").withTimes(1);
|
||||
verifyInvoked("sub").withTimes(1);
|
||||
verifyInvoked("startsWith").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_mock_static_method() {
|
||||
assertEquals("not_secret_box", demoBasic.getBox().get());
|
||||
verify("secretBox").withTimes(1);
|
||||
verifyInvoked("secretBox").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -106,7 +106,7 @@ public class DemoBasicTest {
|
||||
// asynchronous
|
||||
assertEquals("mock_one_mock_others",
|
||||
Executors.newSingleThreadExecutor().submit(() -> demoBasic.callerOne() + "_" + demoBasic.callerTwo()).get());
|
||||
verify("callFromDifferentMethod").withTimes(4);
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,7 +116,7 @@ public class DemoBasicTest {
|
||||
assertEquals("mock_special", demoBasic.callerOne());
|
||||
// asynchronous
|
||||
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoBasic.callerOne()).get());
|
||||
verify("callFromDifferentMethod").withTimes(2);
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.alibaba.testable.demo;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -12,7 +12,7 @@ import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 30)
|
||||
@@ -21,7 +21,7 @@ public class DemoServiceTest {
|
||||
private DemoService demoService;
|
||||
|
||||
public static class Mock {
|
||||
@MockMethod(targetClass = Log.class, targetMethod = "d")
|
||||
@MockInvoke(targetClass = Log.class, targetMethod = "d")
|
||||
public static int log(String tag, String msg) {
|
||||
return 0;
|
||||
}
|
||||
@@ -38,10 +38,10 @@ public class DemoServiceTest {
|
||||
|
||||
intent.setAction("start_foreground");
|
||||
demoService.onStartCommand(intent, 0, 1);
|
||||
verify("log").with("DemoService", "start service.");
|
||||
verifyInvoked("log").with("DemoService", "start service.");
|
||||
|
||||
intent.setAction("stop_foreground");
|
||||
demoService.onStartCommand(intent, 0, 1);
|
||||
verify("log").with("DemoService", "stop service.");
|
||||
verifyInvoked("log").with("DemoService", "stop service.");
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.9')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.9')
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.10')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<junit.version>5.6.2</junit.version>
|
||||
<testable.version>0.6.9</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.alibaba.demo.basic.model.mock.Color;
|
||||
public class DemoInherit {
|
||||
|
||||
/**
|
||||
* call method overridden by sub class via parent class variable
|
||||
* call method overridden by subclass via parent class variable
|
||||
*/
|
||||
public Box putIntoBox() {
|
||||
Box box = new BlackBox("");
|
||||
@@ -20,7 +20,7 @@ public class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method overridden by sub class via sub class variable
|
||||
* call method overridden by subclass via subclass variable
|
||||
*/
|
||||
public BlackBox putIntoBlackBox() {
|
||||
BlackBox box = new BlackBox("");
|
||||
@@ -37,7 +37,7 @@ public class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in parent class via sub class variable
|
||||
* call method defined in parent class via subclass variable
|
||||
*/
|
||||
public String getFromBlackBox() {
|
||||
BlackBox box = new BlackBox("data");
|
||||
@@ -53,10 +53,18 @@ public class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in interface via sub class variable
|
||||
* call method defined in interface via subclass variable
|
||||
*/
|
||||
public String getColorViaBox() {
|
||||
BlackBox box = new BlackBox("");
|
||||
return box.getColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in interface via subclass variable
|
||||
*/
|
||||
public String getColorIdxViaColor() {
|
||||
Color color = new BlackBox("");
|
||||
return color.getColorIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.alibaba.demo.basic.model.mock;
|
||||
|
||||
public interface BasicColor {
|
||||
|
||||
String getColorIndex();
|
||||
|
||||
}
|
||||
@@ -20,4 +20,8 @@ public class BlackBox extends Box implements Color {
|
||||
return "black";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColorIndex() {
|
||||
return "idx";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.alibaba.demo.basic.model.mock;
|
||||
|
||||
public interface Color {
|
||||
public interface Color extends BasicColor {
|
||||
|
||||
String getColor();
|
||||
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
package com.alibaba.demo.lambda;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author jimca
|
||||
*/
|
||||
@SuppressWarnings({"WrapperTypeMayBePrimitive", "ResultOfMethodCallIgnored", "MismatchedReadAndWriteOfArray", "unused"})
|
||||
public class ExternalLambdaDemo {
|
||||
|
||||
public void string1() {
|
||||
String s = "";
|
||||
consumesFunction2(s::contains);
|
||||
}
|
||||
|
||||
public void string2() {
|
||||
String s = "";
|
||||
consumesFunction2(s::charAt);
|
||||
|
||||
}
|
||||
|
||||
public void string3() {
|
||||
String s = "";
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void byte1() {
|
||||
Byte s = 1;
|
||||
consumesSupplier(s::floatValue);
|
||||
}
|
||||
|
||||
public void byte2() {
|
||||
Byte s = 1;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void byte3() {
|
||||
Byte s = 1;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void char1() {
|
||||
Character s = 1;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void char2() {
|
||||
Character s = 1;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void char3() {
|
||||
Character s = 1;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void short1() {
|
||||
Short s = 1;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void short2() {
|
||||
Short s = 1;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void short3() {
|
||||
Short s = 1;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void int1() {
|
||||
Integer s = 1;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void int2() {
|
||||
Integer s = 1;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void int3() {
|
||||
Integer s = 1;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void long1() {
|
||||
Long s = 1L;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void long2() {
|
||||
Long s = 1L;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void long3() {
|
||||
Long s = 1L;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void float1() {
|
||||
Float s = 1f;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void float2() {
|
||||
Float s = 1f;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void float3() {
|
||||
Float s = 1f;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void double1() {
|
||||
Double s = 1d;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void double2() {
|
||||
Double s = 1d;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void double3() {
|
||||
Double s = 1d;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void bool1() {
|
||||
Boolean s = true;
|
||||
consumesFunction0(s::toString);
|
||||
}
|
||||
|
||||
public void bool2() {
|
||||
Boolean s = true;
|
||||
consumesFunction2(s::compareTo);
|
||||
}
|
||||
|
||||
public void bool3() {
|
||||
Boolean s = true;
|
||||
consumesFunction0(s::notify);
|
||||
}
|
||||
|
||||
public void stringArray1() {
|
||||
String[] array = new String[]{""};
|
||||
consumesFunction0(array::toString);
|
||||
}
|
||||
|
||||
public void stringArray2() {
|
||||
String[] array = new String[]{""};
|
||||
consumesFunction2(array::equals);
|
||||
}
|
||||
|
||||
public void stringArray3() {
|
||||
String[] array = new String[]{""};
|
||||
consumesFunction0(array::notify);
|
||||
}
|
||||
|
||||
public void intArray1() {
|
||||
int[] array = new int[]{1};
|
||||
consumesFunction0(array::toString);
|
||||
}
|
||||
|
||||
public void intArray2() {
|
||||
int[] array = new int[]{1};
|
||||
consumesFunction2(array::equals);
|
||||
}
|
||||
|
||||
public void intArray3() {
|
||||
int[] array = new int[]{1};
|
||||
consumesFunction0(array::notify);
|
||||
}
|
||||
|
||||
|
||||
public void mul() {
|
||||
String s = "";
|
||||
consumesTwoFunction2(s::contains, s::contains);
|
||||
}
|
||||
|
||||
|
||||
public void externalClass() {
|
||||
LambdaDemo lambdaDemo = new LambdaDemo();
|
||||
consumesFunction0(lambdaDemo::methodReference0);
|
||||
}
|
||||
|
||||
|
||||
public void interClass() {
|
||||
A a = new A();
|
||||
consumesFunction2(a::m1);
|
||||
consumesFunction2(a::m2);
|
||||
}
|
||||
|
||||
public void function3() {
|
||||
ExternalLambdaDemo externalLambdaDemo = new ExternalLambdaDemo();
|
||||
consumesFunction3(externalLambdaDemo::f3);
|
||||
}
|
||||
|
||||
public Boolean f3(String s1, Long l) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void consumesFunction0(Runnable f) {
|
||||
f.run();
|
||||
}
|
||||
|
||||
private <T> void consumesFunction1(Consumer<T> f) {
|
||||
f.accept(null);
|
||||
}
|
||||
|
||||
private <T, R> void consumesFunction2(Function<T, R> f) {
|
||||
f.apply(null);
|
||||
}
|
||||
|
||||
private <T1, T2, R> void consumesFunction3(BiFunction<T1, T2, R> f) {
|
||||
f.apply(null, null);
|
||||
}
|
||||
|
||||
private <T> void consumesSupplier(Supplier<T> supplier) {
|
||||
supplier.get();
|
||||
}
|
||||
|
||||
private <T, R> void consumesTwoFunction2(Function<T, R> f1, Function<T, R> f2) {
|
||||
f1.apply(null);
|
||||
f2.apply(null);
|
||||
}
|
||||
|
||||
public static class A {
|
||||
public String m1(int i) {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String m2(Integer i) {
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,26 @@
|
||||
package com.alibaba.demo.association;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import com.alibaba.testable.core.model.MockScope;
|
||||
|
||||
class CookerServiceMock {
|
||||
|
||||
@MockMethod(targetClass = CookerService.class)
|
||||
@MockInvoke(targetClass = CookerService.class)
|
||||
public static String hireSandwichCooker() {
|
||||
return "Fake-Sandwich-Cooker";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
|
||||
@MockInvoke(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
|
||||
public static String hireHamburgerCooker() {
|
||||
return "Fake-Hamburger-Cooker";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = CookerService.class)
|
||||
@MockInvoke(targetClass = CookerService.class)
|
||||
private String cookSandwich() {
|
||||
return "Faked-Sandwich";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
|
||||
@MockInvoke(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
|
||||
private String cookHamburger() {
|
||||
return "Faked-Hamburger";
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ package com.alibaba.demo.basic;
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox;
|
||||
import com.alibaba.demo.basic.model.mock.Box;
|
||||
import com.alibaba.demo.basic.model.mock.Color;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
@@ -18,77 +18,88 @@ class DemoInheritTest {
|
||||
private DemoInherit demoInherit = new DemoInherit();
|
||||
|
||||
public static class Mock {
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
private void put_into_box(Box self, String something) {
|
||||
self.put("put_" + something + "_into_box");
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
private void put_into_blackbox(BlackBox self, String something) {
|
||||
self.put("put_" + something + "_into_blackbox");
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "get")
|
||||
@MockInvoke(targetMethod = "get")
|
||||
private String get_from_box(Box self) {
|
||||
return "get_from_box";
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "get")
|
||||
@MockInvoke(targetMethod = "get")
|
||||
private String get_from_blackbox(BlackBox self) {
|
||||
return "get_from_blackbox";
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "getColor")
|
||||
@MockInvoke(targetMethod = "getColor")
|
||||
private String get_color_from_color(Color self) {
|
||||
return "color_from_color";
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "getColor")
|
||||
@MockInvoke(targetMethod = "getColor")
|
||||
private String get_color_from_blackbox(BlackBox self) {
|
||||
return "color_from_blackbox";
|
||||
}
|
||||
|
||||
@MockInvoke(targetMethod = "getColorIndex")
|
||||
private String get_colorIdx_from_color(Color self) {
|
||||
return "colorIdx_from_color";
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_sub_object_method_by_parent_object() {
|
||||
BlackBox box = (BlackBox)demoInherit.putIntoBox();
|
||||
verify("put_into_box").withTimes(1);
|
||||
verifyInvoked("put_into_box").withTimes(1);
|
||||
assertEquals("put_data_into_box", box.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_sub_object_method_by_sub_object() {
|
||||
BlackBox box = demoInherit.putIntoBlackBox();
|
||||
verify("put_into_blackbox").withTimes(1);
|
||||
verifyInvoked("put_into_blackbox").withTimes(1);
|
||||
assertEquals("put_data_into_blackbox", box.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_parent_object_method_by_parent_object() {
|
||||
String content = demoInherit.getFromBox();
|
||||
verify("get_from_box").withTimes(1);
|
||||
verifyInvoked("get_from_box").withTimes(1);
|
||||
assertEquals("get_from_box", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_parent_object_method_by_sub_object() {
|
||||
String content = demoInherit.getFromBlackBox();
|
||||
verify("get_from_blackbox").withTimes(1);
|
||||
verifyInvoked("get_from_blackbox").withTimes(1);
|
||||
assertEquals("get_from_blackbox", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_interface_method_by_interface_object() {
|
||||
String color = demoInherit.getColorViaColor();
|
||||
verify("get_color_from_color").withTimes(1);
|
||||
verifyInvoked("get_color_from_color").withTimes(1);
|
||||
assertEquals("color_from_color", color);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_interface_method_by_sub_class_object() {
|
||||
String color = demoInherit.getColorViaBox();
|
||||
verify("get_color_from_blackbox").withTimes(1);
|
||||
verifyInvoked("get_color_from_blackbox").withTimes(1);
|
||||
assertEquals("color_from_blackbox", color);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_call_interface_method_by_sub_interface_object() {
|
||||
String colorIdx = demoInherit.getColorIdxViaColor();
|
||||
verifyInvoked("get_colorIdx_from_color").withTimes(1);
|
||||
assertEquals("colorIdx_from_color", colorIdx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.alibaba.demo.basic;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -12,7 +12,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
class DemoInnerClassTest {
|
||||
|
||||
public static class Mock {
|
||||
@MockMethod(targetClass = DemoInnerClass.class)
|
||||
@MockInvoke(targetClass = DemoInnerClass.class)
|
||||
String methodToBeMock() {
|
||||
return "MockedCall";
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.alibaba.demo.basic;
|
||||
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import com.alibaba.testable.core.error.VerifyFailedError;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeMatcher.*;
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationMatcher.*;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
@@ -18,62 +18,62 @@ class DemoMatcherTest {
|
||||
private DemoMatcher demoMatcher = new DemoMatcher();
|
||||
|
||||
public static class Mock {
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private void methodWithoutArgument(DemoMatcher self) {}
|
||||
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private void methodWithArguments(DemoMatcher self, Object a1, Object a2) {}
|
||||
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private void methodWithArrayArgument(DemoMatcher self, Object[] a) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_no_argument() {
|
||||
demoMatcher.callMethodWithoutArgument();
|
||||
verify("methodWithoutArgument").withTimes(1);
|
||||
verifyInvoked("methodWithoutArgument").withTimes(1);
|
||||
demoMatcher.callMethodWithoutArgument();
|
||||
verify("methodWithoutArgument").withTimes(2);
|
||||
verifyInvoked("methodWithoutArgument").withTimes(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_number_arguments() {
|
||||
demoMatcher.callMethodWithNumberArguments();
|
||||
verify("methodWithArguments").without(anyString(), 2);
|
||||
verify("methodWithArguments").withInOrder(anyInt(), 2);
|
||||
verify("methodWithArguments").withInOrder(anyLong(), anyNumber());
|
||||
verify("methodWithArguments").with(1.0, anyMapOf(Integer.class, Float.class));
|
||||
verify("methodWithArguments").with(anyList(), anySetOf(Float.class));
|
||||
verify("methodWithArguments").with(anyList(), anyListOf(Float.class));
|
||||
verify("methodWithArrayArgument").with(anyArrayOf(Long.class));
|
||||
verify("methodWithArrayArgument").with(anyArray());
|
||||
verifyInvoked("methodWithArguments").without(anyString(), 2);
|
||||
verifyInvoked("methodWithArguments").withInOrder(anyInt(), 2);
|
||||
verifyInvoked("methodWithArguments").withInOrder(anyLong(), anyNumber());
|
||||
verifyInvoked("methodWithArguments").with(1.0, anyMapOf(Integer.class, Float.class));
|
||||
verifyInvoked("methodWithArguments").with(anyList(), anySetOf(Float.class));
|
||||
verifyInvoked("methodWithArguments").with(anyList(), anyListOf(Float.class));
|
||||
verifyInvoked("methodWithArrayArgument").with(anyArrayOf(Long.class));
|
||||
verifyInvoked("methodWithArrayArgument").with(anyArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_string_arguments() {
|
||||
demoMatcher.callMethodWithStringArgument();
|
||||
verify("methodWithArguments").with(startsWith("he"), endsWith("ld"));
|
||||
verify("methodWithArguments").with(contains("stab"), matches("m.[cd]k"));
|
||||
verify("methodWithArrayArgument").with(anyArrayOf(String.class));
|
||||
verifyInvoked("methodWithArguments").with(startsWith("he"), endsWith("ld"));
|
||||
verifyInvoked("methodWithArguments").with(contains("stab"), matches("m.[cd]k"));
|
||||
verifyInvoked("methodWithArrayArgument").with(anyArrayOf(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_object_arguments() {
|
||||
demoMatcher.callMethodWithObjectArgument();
|
||||
verify("methodWithArguments").withInOrder(any(BlackBox.class), any(BlackBox.class));
|
||||
verify("methodWithArguments").withInOrder(nullable(BlackBox.class), nullable(BlackBox.class));
|
||||
verify("methodWithArguments").withInOrder(isNull(), notNull());
|
||||
verifyInvoked("methodWithArguments").withInOrder(any(BlackBox.class), any(BlackBox.class));
|
||||
verifyInvoked("methodWithArguments").withInOrder(nullable(BlackBox.class), nullable(BlackBox.class));
|
||||
verifyInvoked("methodWithArguments").withInOrder(isNull(), notNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_match_with_times() {
|
||||
demoMatcher.callMethodWithNumberArguments();
|
||||
verify("methodWithArguments").with(anyNumber(), any()).times(3);
|
||||
verifyInvoked("methodWithArguments").with(anyNumber(), any()).times(3);
|
||||
|
||||
demoMatcher.callMethodWithNumberArguments();
|
||||
boolean gotError = false;
|
||||
try {
|
||||
verify("methodWithArguments").with(anyNumber(), any()).times(4);
|
||||
verifyInvoked("methodWithArguments").with(anyNumber(), any()).times(4);
|
||||
} catch (VerifyFailedError e) {
|
||||
gotError = true;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.alibaba.demo.basic;
|
||||
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox;
|
||||
import com.alibaba.testable.core.annotation.MockConstructor;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockNew;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static com.alibaba.testable.core.tool.TestableTool.MOCK_CONTEXT;
|
||||
import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -21,42 +21,42 @@ class DemoMockTest {
|
||||
private DemoMock demoMock = new DemoMock();
|
||||
|
||||
public static class Mock {
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
private BlackBox createBlackBox(String text) {
|
||||
return new BlackBox("mock_" + text);
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoMock.class)
|
||||
@MockInvoke(targetClass = DemoMock.class)
|
||||
private String innerFunc(String text) {
|
||||
return "mock_" + text;
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoMock.class)
|
||||
@MockInvoke(targetClass = DemoMock.class)
|
||||
private String staticFunc() {
|
||||
return "_MOCK_TAIL";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = String.class)
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private String trim() {
|
||||
return "trim_string";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = String.class, targetMethod = "substring")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "substring")
|
||||
private String sub(int i, int j) {
|
||||
return "sub_string";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = String.class)
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private boolean startsWith(String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = BlackBox.class)
|
||||
@MockInvoke(targetClass = BlackBox.class)
|
||||
private BlackBox secretBox() {
|
||||
return new BlackBox("not_secret_box");
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoMock.class)
|
||||
@MockInvoke(targetClass = DemoMock.class)
|
||||
private String callFromDifferentMethod() {
|
||||
if ("special_case".equals(MOCK_CONTEXT.get("case"))) {
|
||||
return "mock_special";
|
||||
@@ -73,28 +73,28 @@ class DemoMockTest {
|
||||
@Test
|
||||
void should_mock_new_object() {
|
||||
assertEquals("mock_something", demoMock.newFunc());
|
||||
verify("createBlackBox").with("something");
|
||||
verifyInvoked("createBlackBox").with("something");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_member_method() throws Exception {
|
||||
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"));
|
||||
verify("innerFunc").with("hello");
|
||||
verify("staticFunc").with();
|
||||
verifyInvoked("innerFunc").with("hello");
|
||||
verifyInvoked("staticFunc").with();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_common_method() {
|
||||
assertEquals("trim_string__sub_string__false", demoMock.commonFunc());
|
||||
verify("trim").withTimes(1);
|
||||
verify("sub").withTimes(1);
|
||||
verify("startsWith").withTimes(1);
|
||||
verifyInvoked("trim").withTimes(1);
|
||||
verifyInvoked("sub").withTimes(1);
|
||||
verifyInvoked("startsWith").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_mock_static_method() {
|
||||
assertEquals("not_secret_box", demoMock.getBox().get());
|
||||
verify("secretBox").withTimes(1);
|
||||
verifyInvoked("secretBox").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,7 +104,7 @@ class DemoMockTest {
|
||||
// asynchronous
|
||||
assertEquals("mock_one_mock_others",
|
||||
Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne() + "_" + demoMock.callerTwo()).get());
|
||||
verify("callFromDifferentMethod").withTimes(4);
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,7 +114,7 @@ class DemoMockTest {
|
||||
assertEquals("mock_special", demoMock.callerOne());
|
||||
// asynchronous
|
||||
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne()).get());
|
||||
verify("callFromDifferentMethod").withTimes(2);
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.alibaba.demo.basic;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockConstructor;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.demo.basic.DemoTemplate;
|
||||
import com.alibaba.testable.core.annotation.MockNew;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
@@ -21,24 +20,24 @@ class DemoTemplateTest {
|
||||
/* 第一种写法:使用泛型定义 */
|
||||
/* First solution: use generics type */
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private <T> List<T> getList(DemoTemplate self, T value) {
|
||||
return new ArrayList<T>() {{ add((T)(value.toString() + "_mock_list")); }};
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
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")); }};
|
||||
}
|
||||
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
private <T> HashSet<T> newHashSet() {
|
||||
HashSet<T> set = new HashSet<>();
|
||||
set.add((T)"insert_mock");
|
||||
return set;
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private <E> boolean add(Set s, E e) {
|
||||
s.add(e.toString() + "_mocked");
|
||||
return true;
|
||||
@@ -47,24 +46,24 @@ class DemoTemplateTest {
|
||||
/* 第二种写法:使用Object类型 */
|
||||
/* Second solution: use object type */
|
||||
|
||||
//@MockMethod
|
||||
//@MockInvoke
|
||||
//private List<Object> getList(DemoTemplate self, Object value) {
|
||||
// return new ArrayList<Object>() {{ add(value.toString() + "_mock_list"); }};
|
||||
//}
|
||||
//
|
||||
//@MockMethod
|
||||
//@MockInvoke
|
||||
//private Map<Object, Object> getMap(DemoTemplate self, Object key, Object value) {
|
||||
// return new HashMap<Object, Object>() {{ put(key, value.toString() + "_mock_map"); }};
|
||||
//}
|
||||
//
|
||||
//@MockConstructor
|
||||
//@MockNew
|
||||
//private HashSet newHashSet() {
|
||||
// HashSet<Object> set = new HashSet<>();
|
||||
// set.add("insert_mock");
|
||||
// return set;
|
||||
//}
|
||||
//
|
||||
//@MockMethod
|
||||
//@MockInvoke
|
||||
//private boolean add(Set s, Object e) {
|
||||
// s.add(e.toString() + "_mocked");
|
||||
// return true;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.alibaba.demo.lambda;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
|
||||
/**
|
||||
* @author zcbbpo
|
||||
*/
|
||||
public class ExternalLambdaDemoTest {
|
||||
private final ExternalLambdaDemo lambdaDemo = new ExternalLambdaDemo();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class Mock {
|
||||
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "contains")
|
||||
public boolean mockContains(CharSequence s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = Byte.class, targetMethod = "floatValue")
|
||||
public float mockFloatValue() {
|
||||
return 0.1f;
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = Double.class, targetMethod = "compareTo")
|
||||
public int mockCompareTo(Double anotherDouble) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = LambdaDemo.class, targetMethod = "methodReference0")
|
||||
public String mockMethodReference0() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@MockInvoke(targetClass = ExternalLambdaDemo.class, targetMethod = "f3")
|
||||
public Boolean mockF3(String s1, Long l) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldMockString1() {
|
||||
lambdaDemo.string1();
|
||||
verifyInvoked("mockContains").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockByte1() {
|
||||
lambdaDemo.byte1();
|
||||
verifyInvoked("mockFloatValue").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMockDouble2() {
|
||||
lambdaDemo.double2();
|
||||
verifyInvoked("mockCompareTo").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMul() {
|
||||
lambdaDemo.mul();
|
||||
verifyInvoked("mockContains").withTimes(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalClass() {
|
||||
lambdaDemo.externalClass();
|
||||
verifyInvoked("mockMethodReference0").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunction3() {
|
||||
lambdaDemo.function3();
|
||||
verifyInvoked("mockF3").withTimes(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +1,45 @@
|
||||
package com.alibaba.demo.lambda;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockDiagnose;
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.model.LogLevel;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* @author zcbbpo
|
||||
*/
|
||||
public class LambdaDemoTest {
|
||||
private LambdaDemo lambdaDemo = new LambdaDemo();
|
||||
private final LambdaDemo lambdaDemo = new LambdaDemo();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@MockDiagnose(LogLevel.VERBOSE)
|
||||
public static class Mock {
|
||||
@MockMethod(targetClass = LambdaDemo.class, targetMethod = "run")
|
||||
@MockInvoke(targetClass = LambdaDemo.class, targetMethod = "run")
|
||||
private void mockRun() {
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = LambdaDemo.class)
|
||||
@MockInvoke(targetClass = LambdaDemo.class)
|
||||
private String function0() {
|
||||
return "mock_function0";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = LambdaDemo.class)
|
||||
@MockInvoke(targetClass = LambdaDemo.class)
|
||||
private String function1(Integer i) {
|
||||
return "mock_function1";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = LambdaDemo.class)
|
||||
@MockInvoke(targetClass = LambdaDemo.class)
|
||||
private String function2(Integer i, Double d) {
|
||||
return "mock_function2";
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
@MockMethod(targetClass = LambdaDemo.class)
|
||||
@MockInvoke(targetClass = LambdaDemo.class)
|
||||
private String function1Throwable(Integer i) throws Throwable{
|
||||
return "mock_function1Throwable";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = StaticMethod.class, targetMethod = "function1")
|
||||
@MockInvoke(targetClass = StaticMethod.class, targetMethod = "function1")
|
||||
public static String staticFunction1(Integer i) {
|
||||
return "mock_staticFunction1";
|
||||
}
|
||||
@@ -53,7 +50,7 @@ public class LambdaDemoTest {
|
||||
@Test
|
||||
public void shouldMockRun() {
|
||||
lambdaDemo.methodReference();
|
||||
verify("mockRun").withTimes(1);
|
||||
verifyInvoked("mockRun").withTimes(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
public class ASvcMock {
|
||||
|
||||
@MockMethod(targetClass = String.class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "format")
|
||||
public String a_format(String format, Object... args) {
|
||||
return "a_mock";
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
public class BSvcMock {
|
||||
|
||||
@MockMethod(targetClass = String.class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "format")
|
||||
public String b_format(String format, Object... args) {
|
||||
return "b_mock";
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi;
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod;
|
||||
import com.alibaba.testable.core.annotation.MockInvoke;
|
||||
|
||||
public class CSvcMock {
|
||||
|
||||
@MockMethod(targetClass = String.class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "format")
|
||||
public String c_format(String format, Object... args) {
|
||||
return "c_mock";
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.alibaba.testable.core.annotation.MockWith;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@MockWith
|
||||
@@ -19,9 +19,9 @@ public class OneToMultiSvcTest {
|
||||
assertEquals("a_mock", aSvc.demo("test"));
|
||||
assertEquals("b_mock", bSvc.demo("test"));
|
||||
assertEquals("c_mock", cSvc.demo("test"));
|
||||
verify("a_format").withTimes(1);
|
||||
verify("b_format").withTimes(1);
|
||||
verify("c_format").withTimes(1);
|
||||
verifyInvoked("a_format").withTimes(1);
|
||||
verifyInvoked("b_format").withTimes(1);
|
||||
verifyInvoked("c_format").withTimes(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.6.2")
|
||||
testImplementation("com.alibaba.testable:testable-all:0.6.9")
|
||||
testAnnotationProcessor("com.alibaba.testable:testable-processor:0.6.9")
|
||||
testImplementation("com.alibaba.testable:testable-all:0.6.10")
|
||||
testAnnotationProcessor("com.alibaba.testable:testable-processor:0.6.10")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<junit.version>5.6.2</junit.version>
|
||||
<testable.version>0.6.9</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.alibaba.demo.basic.model.mock.Color
|
||||
class DemoInherit {
|
||||
|
||||
/**
|
||||
* call method overridden by sub class via parent class variable
|
||||
* call method overridden by subclass via parent class variable
|
||||
*/
|
||||
fun putIntoBox(): Box {
|
||||
val box: Box = BlackBox("")
|
||||
@@ -20,7 +20,7 @@ class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method overridden by sub class via sub class variable
|
||||
* call method overridden by subclass via subclass variable
|
||||
*/
|
||||
fun putIntoBlackBox(): BlackBox {
|
||||
val box = BlackBox("")
|
||||
@@ -38,7 +38,7 @@ class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in parent class via sub class variable
|
||||
* call method defined in parent class via subclass variable
|
||||
*/
|
||||
val fromBlackBox: String?
|
||||
get() {
|
||||
@@ -56,7 +56,7 @@ class DemoInherit {
|
||||
}
|
||||
|
||||
/**
|
||||
* call method defined in interface via sub class variable
|
||||
* call method defined in interface via subclass variable
|
||||
*/
|
||||
val colorViaBox: String
|
||||
get() {
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package com.alibaba.demo.association
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.model.MockScope
|
||||
|
||||
internal class CookerServiceMock {
|
||||
|
||||
@MockMethod(targetClass = CookerService::class)
|
||||
@MockInvoke(targetClass = CookerService::class)
|
||||
private fun cookSandwich(): String {
|
||||
return "Faked-Sandwich"
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
|
||||
@MockInvoke(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
|
||||
private fun cookHamburger(): String {
|
||||
return "Faked-Hamburger"
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = CookerService::class)
|
||||
@MockInvoke(targetClass = CookerService::class)
|
||||
fun hireSandwichCooker(): String {
|
||||
return "Fake-Sandwich-Cooker"
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
|
||||
@MockInvoke(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
|
||||
fun hireHamburgerCooker(): String {
|
||||
return "Fake-Hamburger-Cooker"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.matcher.InvokeVerifier
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox
|
||||
import com.alibaba.demo.basic.model.mock.Box
|
||||
import com.alibaba.demo.basic.model.mock.Color
|
||||
@@ -17,32 +17,32 @@ internal class DemoInheritTest {
|
||||
private val demoInherit = DemoInherit()
|
||||
|
||||
class Mock {
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
private fun put_into_box(self: Box, something: String) {
|
||||
self.put("put_" + something + "_into_box")
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
private fun put_into_blackbox(self: BlackBox, something: String) {
|
||||
self.put("put_" + something + "_into_blackbox")
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "get")
|
||||
@MockInvoke(targetMethod = "get")
|
||||
private fun get_from_box(self: Box): String {
|
||||
return "get_from_box"
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "get")
|
||||
@MockInvoke(targetMethod = "get")
|
||||
private fun get_from_blackbox(self: BlackBox): String {
|
||||
return "get_from_blackbox"
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "getColor")
|
||||
@MockInvoke(targetMethod = "getColor")
|
||||
private fun get_color_from_color(self: Color): String {
|
||||
return "color_from_color"
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "getColor")
|
||||
@MockInvoke(targetMethod = "getColor")
|
||||
private fun get_color_from_blackbox(self: BlackBox): String {
|
||||
return "color_from_blackbox"
|
||||
}
|
||||
@@ -51,42 +51,42 @@ internal class DemoInheritTest {
|
||||
@Test
|
||||
fun should_mock_call_sub_object_method_by_parent_object() {
|
||||
val box = demoInherit.putIntoBox() as BlackBox
|
||||
InvokeVerifier.verify("put_into_box").withTimes(1)
|
||||
InvocationVerifier.verifyInvoked("put_into_box").withTimes(1)
|
||||
assertEquals("put_data_into_box", box.get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_sub_object_method_by_sub_object() {
|
||||
val box = demoInherit.putIntoBlackBox()
|
||||
InvokeVerifier.verify("put_into_blackbox").withTimes(1)
|
||||
InvocationVerifier.verifyInvoked("put_into_blackbox").withTimes(1)
|
||||
assertEquals("put_data_into_blackbox", box.get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_parent_object_method_by_parent_object() {
|
||||
val content = demoInherit.fromBox
|
||||
InvokeVerifier.verify("get_from_box").withTimes(1)
|
||||
InvocationVerifier.verifyInvoked("get_from_box").withTimes(1)
|
||||
assertEquals("get_from_box", content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_parent_object_method_by_sub_object() {
|
||||
val content = demoInherit.fromBlackBox
|
||||
InvokeVerifier.verify("get_from_blackbox").withTimes(1)
|
||||
InvocationVerifier.verifyInvoked("get_from_blackbox").withTimes(1)
|
||||
assertEquals("get_from_blackbox", content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_interface_method_by_interface_object() {
|
||||
val color = demoInherit.colorViaColor
|
||||
InvokeVerifier.verify("get_color_from_color").withTimes(1)
|
||||
InvocationVerifier.verifyInvoked("get_color_from_color").withTimes(1)
|
||||
assertEquals("color_from_color", color)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_call_interface_method_by_sub_class_object() {
|
||||
val color = demoInherit.colorViaBox
|
||||
InvokeVerifier.verify("get_color_from_blackbox").withTimes(1)
|
||||
InvocationVerifier.verifyInvoked("get_color_from_blackbox").withTimes(1)
|
||||
assertEquals("color_from_blackbox", color)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test
|
||||
internal class DemoInnerClassTest {
|
||||
|
||||
class Mock {
|
||||
@MockMethod(targetClass = DemoInnerClass::class)
|
||||
@MockInvoke(targetClass = DemoInnerClass::class)
|
||||
fun methodToBeMock(): String {
|
||||
return "MockedCall"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.error.VerifyFailedError
|
||||
import com.alibaba.testable.core.matcher.InvokeMatcher
|
||||
import com.alibaba.testable.core.matcher.InvokeVerifier
|
||||
import com.alibaba.testable.core.matcher.InvocationMatcher
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -17,15 +17,15 @@ internal class DemoMatcherTest {
|
||||
private val demoMatcher = DemoMatcher()
|
||||
|
||||
class Mock {
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private fun methodWithoutArgument(self: DemoMatcher) {
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private fun methodWithArguments(self: DemoMatcher, a1: Any, a2: Any) {
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "methodToBeMocked")
|
||||
@MockInvoke(targetMethod = "methodToBeMocked")
|
||||
private fun methodWithArrayArgument(self: DemoMatcher, a: Array<Any>) {
|
||||
}
|
||||
}
|
||||
@@ -33,50 +33,50 @@ internal class DemoMatcherTest {
|
||||
@Test
|
||||
fun should_match_no_argument() {
|
||||
demoMatcher.callMethodWithoutArgument()
|
||||
InvokeVerifier.verify("methodWithoutArgument").withTimes(1)
|
||||
InvocationVerifier.verifyInvoked("methodWithoutArgument").withTimes(1)
|
||||
demoMatcher.callMethodWithoutArgument()
|
||||
InvokeVerifier.verify("methodWithoutArgument").withTimes(2)
|
||||
InvocationVerifier.verifyInvoked("methodWithoutArgument").withTimes(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_match_number_arguments() {
|
||||
demoMatcher.callMethodWithNumberArguments()
|
||||
InvokeVerifier.verify("methodWithArguments").without(InvokeMatcher.anyString(), 2)
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.anyInt(), 2)
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.anyLong(), InvokeMatcher.anyNumber())
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").without(InvocationMatcher.anyString(), 2)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.anyInt(), 2)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.anyLong(), InvocationMatcher.anyNumber())
|
||||
// Note: Must use `::class.javaObjectType` for primary types check in Kotlin
|
||||
InvokeVerifier.verify("methodWithArguments").with(1.0, InvokeMatcher.anyMapOf(Int::class.javaObjectType, Float::class.javaObjectType)).times(2)
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyList(), InvokeMatcher.anySetOf(Float::class.javaObjectType)).times(2)
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyList(), InvokeMatcher.anyListOf(Float::class.javaObjectType))
|
||||
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArrayOf(Long::class.javaObjectType))
|
||||
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArray())
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(1.0, InvocationMatcher.anyMapOf(Int::class.javaObjectType, Float::class.javaObjectType)).times(2)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyList(), InvocationMatcher.anySetOf(Float::class.javaObjectType)).times(2)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyList(), InvocationMatcher.anyListOf(Float::class.javaObjectType))
|
||||
InvocationVerifier.verifyInvoked("methodWithArrayArgument").with(InvocationMatcher.anyArrayOf(Long::class.javaObjectType))
|
||||
InvocationVerifier.verifyInvoked("methodWithArrayArgument").with(InvocationMatcher.anyArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_match_string_arguments() {
|
||||
demoMatcher.callMethodWithStringArgument()
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.startsWith("he"), InvokeMatcher.endsWith("ld"))
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.contains("stab"), InvokeMatcher.matches("m.[cd]k"))
|
||||
InvokeVerifier.verify("methodWithArrayArgument").with(InvokeMatcher.anyArrayOf(String::class.java))
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.startsWith("he"), InvocationMatcher.endsWith("ld"))
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.contains("stab"), InvocationMatcher.matches("m.[cd]k"))
|
||||
InvocationVerifier.verifyInvoked("methodWithArrayArgument").with(InvocationMatcher.anyArrayOf(String::class.java))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_match_object_arguments() {
|
||||
demoMatcher.callMethodWithObjectArgument()
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.any(BlackBox::class.java), InvokeMatcher.any(BlackBox::class.java))
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.nullable(BlackBox::class.java), InvokeMatcher.nullable(BlackBox::class.java))
|
||||
InvokeVerifier.verify("methodWithArguments").withInOrder(InvokeMatcher.isNull(), InvokeMatcher.notNull())
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.any(BlackBox::class.java), InvocationMatcher.any(BlackBox::class.java))
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.nullable(BlackBox::class.java), InvocationMatcher.nullable(BlackBox::class.java))
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").withInOrder(InvocationMatcher.isNull(), InvocationMatcher.notNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_match_with_times() {
|
||||
demoMatcher.callMethodWithNumberArguments()
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(4)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyNumber(), InvocationMatcher.any()).times(4)
|
||||
|
||||
demoMatcher.callMethodWithNumberArguments()
|
||||
var gotError = false
|
||||
try {
|
||||
InvokeVerifier.verify("methodWithArguments").with(InvokeMatcher.anyNumber(), InvokeMatcher.any()).times(5)
|
||||
InvocationVerifier.verifyInvoked("methodWithArguments").with(InvocationMatcher.anyNumber(), InvocationMatcher.any()).times(5)
|
||||
} catch (e: VerifyFailedError) {
|
||||
gotError = true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
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.annotation.MockNew
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
|
||||
import com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD
|
||||
import com.alibaba.testable.core.tool.TestableTool.MOCK_CONTEXT
|
||||
import com.alibaba.demo.basic.model.mock.BlackBox
|
||||
@@ -20,37 +20,37 @@ internal class DemoMockTest {
|
||||
private val demoMock = DemoMock()
|
||||
|
||||
class Mock {
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
private fun createBlackBox(text: String) = BlackBox("mock_$text")
|
||||
|
||||
@MockMethod(targetClass = DemoMock::class)
|
||||
@MockInvoke(targetClass = DemoMock::class)
|
||||
private fun innerFunc(text: String) = "mock_$text"
|
||||
|
||||
@MockMethod(targetClass = DemoMock::class)
|
||||
@MockInvoke(targetClass = DemoMock::class)
|
||||
private fun staticFunc(): String {
|
||||
return "_MOCK_TAIL";
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = BlackBox::class)
|
||||
@MockInvoke(targetClass = BlackBox::class)
|
||||
private fun trim() = "trim_string"
|
||||
|
||||
@MockMethod(targetClass = BlackBox::class, targetMethod = "substring")
|
||||
@MockInvoke(targetClass = BlackBox::class, targetMethod = "substring")
|
||||
private fun sub(i: Int, j: Int) = "sub_string"
|
||||
|
||||
@MockMethod(targetClass = BlackBox::class)
|
||||
@MockInvoke(targetClass = BlackBox::class)
|
||||
private fun startsWith(s: String) = false
|
||||
|
||||
@MockMethod(targetClass = BlackBox::class)
|
||||
@MockInvoke(targetClass = BlackBox::class)
|
||||
private fun secretBox(): BlackBox {
|
||||
return BlackBox("not_secret_box")
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = ColorBox::class)
|
||||
@MockInvoke(targetClass = ColorBox::class)
|
||||
private fun createBox(color: String, box: BlackBox): BlackBox {
|
||||
return BlackBox("White_${box.get()}")
|
||||
}
|
||||
|
||||
@MockMethod(targetClass = DemoMock::class)
|
||||
@MockInvoke(targetClass = DemoMock::class)
|
||||
private fun callFromDifferentMethod(): String {
|
||||
return if (MOCK_CONTEXT["case"] == "special_case") {
|
||||
"mock_special"
|
||||
@@ -66,35 +66,35 @@ internal class DemoMockTest {
|
||||
@Test
|
||||
fun should_mock_new_object() {
|
||||
assertEquals("mock_something", demoMock.newFunc())
|
||||
verify("createBlackBox").with("something")
|
||||
verifyInvoked("createBlackBox").with("something")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_member_method() {
|
||||
assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello"))
|
||||
verify("innerFunc").with("hello")
|
||||
verify("staticFunc").with()
|
||||
verifyInvoked("innerFunc").with("hello")
|
||||
verifyInvoked("staticFunc").with()
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun should_mock_method_in_companion_object() {
|
||||
// assertEquals("CALL_MOCK_TAIL", DemoMock.callStaticFunc())
|
||||
// verify("staticFunc").with()
|
||||
// verifyInvoked("staticFunc").with()
|
||||
// }
|
||||
|
||||
@Test
|
||||
fun should_mock_common_method() {
|
||||
assertEquals("trim_string__sub_string__false", demoMock.commonFunc())
|
||||
verify("trim").withTimes(1)
|
||||
verify("sub").withTimes(1)
|
||||
verify("startsWith").withTimes(1)
|
||||
verifyInvoked("trim").withTimes(1)
|
||||
verifyInvoked("sub").withTimes(1)
|
||||
verifyInvoked("startsWith").withTimes(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_mock_static_method() {
|
||||
assertEquals("White_not_secret_box", demoMock.getBox().get())
|
||||
verify("secretBox").withTimes(1)
|
||||
verify("createBox").withTimes(1)
|
||||
verifyInvoked("secretBox").withTimes(1)
|
||||
verifyInvoked("createBox").withTimes(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,7 +105,7 @@ internal class DemoMockTest {
|
||||
assertEquals("mock_one_mock_others", Executors.newSingleThreadExecutor().submit<String> {
|
||||
demoMock.callerOne() + "_" + demoMock.callerTwo()
|
||||
}.get())
|
||||
verify("callFromDifferentMethod").withTimes(4)
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(4)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -117,7 +117,7 @@ internal class DemoMockTest {
|
||||
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit<String> {
|
||||
demoMock.callerOne()
|
||||
}.get())
|
||||
verify("callFromDifferentMethod").withTimes(2)
|
||||
verifyInvoked("callFromDifferentMethod").withTimes(2)
|
||||
MOCK_CONTEXT.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.alibaba.demo.basic
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockConstructor
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockNew
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@@ -14,24 +14,24 @@ internal class DemoTemplateTest {
|
||||
private val demoTemplate = DemoTemplate()
|
||||
|
||||
class Mock {
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private fun <T> getList(self: DemoTemplate, value: T): List<T> {
|
||||
return mutableListOf((value.toString() + "_mock_list") as T)
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private fun <K, V> getMap(self: DemoTemplate, key: K, value: V): Map<K, V> {
|
||||
return mutableMapOf(key to (value.toString() + "_mock_map") as V)
|
||||
}
|
||||
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
private fun newHashSet(): HashSet<*> {
|
||||
val set = HashSet<Any>()
|
||||
set.add("insert_mock")
|
||||
return set
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private fun <E> add(s: MutableSet<E>, e: E): Boolean {
|
||||
s.add((e.toString() + "_mocked") as E)
|
||||
return true
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.alibaba.demo.java2kotlin
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
|
||||
class PathDemoTest {
|
||||
|
||||
class Mock {
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
fun exists(f: File): Boolean {
|
||||
return when (f.absolutePath) {
|
||||
"/a/b" -> true
|
||||
@@ -17,7 +17,7 @@ class PathDemoTest {
|
||||
}
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
fun isDirectory(f: File): Boolean {
|
||||
return when (f.absolutePath) {
|
||||
"/a/b/c" -> true
|
||||
@@ -25,12 +25,12 @@ class PathDemoTest {
|
||||
}
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
fun delete(f: File): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
fun listFiles(f: File): Array<File>? {
|
||||
return when (f.absolutePath) {
|
||||
"/a/b" -> arrayOf(File("/a/b/c"), File("/a/b/d"))
|
||||
@@ -43,8 +43,8 @@ class PathDemoTest {
|
||||
@Test
|
||||
fun should_mock_java_method_invoke_in_kotlin() {
|
||||
PathDemo.deleteRecursively(File("/a/b/"))
|
||||
verify("listFiles").withTimes(2)
|
||||
verify("delete").withTimes(4)
|
||||
verifyInvoked("listFiles").withTimes(2)
|
||||
verifyInvoked("delete").withTimes(4)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
|
||||
class ASvcMock {
|
||||
|
||||
@MockMethod(targetClass = String::class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String::class, targetMethod = "format")
|
||||
fun a_format(format: String, vararg args: Any?): String {
|
||||
return "a_mock"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
|
||||
class BSvcMock {
|
||||
|
||||
@MockMethod(targetClass = String::class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String::class, targetMethod = "format")
|
||||
fun b_format(format: String, vararg args: Any?): String {
|
||||
return "b_mock"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.alibaba.demo.one2multi
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
|
||||
class CSvcMock {
|
||||
|
||||
@MockMethod(targetClass = String::class, targetMethod = "format")
|
||||
@MockInvoke(targetClass = String::class, targetMethod = "format")
|
||||
fun c_format(format: String, vararg args: Any?): String {
|
||||
return "c_mock"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.alibaba.demo.one2multi
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockWith
|
||||
import com.alibaba.testable.core.matcher.InvokeVerifier.verify
|
||||
import com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@@ -17,9 +17,9 @@ class OneToMultiSvcTest {
|
||||
assertEquals("a_mock", aSvc.demo("test"))
|
||||
assertEquals("b_mock", bSvc.demo("test"))
|
||||
assertEquals("c_mock", cSvc.demo("test"))
|
||||
verify("a_format").withTimes(1)
|
||||
verify("b_format").withTimes(1)
|
||||
verify("c_format").withTimes(1)
|
||||
verifyInvoked("a_format").withTimes(1)
|
||||
verifyInvoked("b_format").withTimes(1)
|
||||
verifyInvoked("c_format").withTimes(1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ repositories {
|
||||
dependencies {
|
||||
testImplementation 'org.codehaus.groovy:groovy-all:3.0.7'
|
||||
testImplementation 'org.spockframework:spock-core:2.0-M5-groovy-3.0'
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.9')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.9')
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.10')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<testable.version>0.6.9</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.github.pbetkier.spockdemo
|
||||
|
||||
import com.alibaba.testable.core.annotation.MockConstructor
|
||||
import com.alibaba.testable.core.annotation.MockMethod
|
||||
import com.alibaba.testable.core.annotation.MockNew
|
||||
import com.alibaba.testable.core.annotation.MockInvoke
|
||||
import com.github.pbetkier.spockdemo.model.SpockBox
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Specification
|
||||
|
||||
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
|
||||
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
|
||||
|
||||
class DemoSpockTest extends Specification {
|
||||
|
||||
@@ -14,14 +14,14 @@ class DemoSpockTest extends Specification {
|
||||
def demoSpock = new DemoSpock()
|
||||
|
||||
static class Mock {
|
||||
@MockConstructor
|
||||
@MockNew
|
||||
SpockBox createBox() {
|
||||
SpockBox box = new SpockBox()
|
||||
box.put("mock zero")
|
||||
return box
|
||||
}
|
||||
|
||||
@MockMethod(targetMethod = "put")
|
||||
@MockInvoke(targetMethod = "put")
|
||||
void putBox(SpockBox self, String data) {
|
||||
self.put("mock " + data)
|
||||
}
|
||||
@@ -37,8 +37,8 @@ class DemoSpockTest extends Specification {
|
||||
box.pop() == "mock 2"
|
||||
box.pop() == "mock 1"
|
||||
box.pop() == "mock zero"
|
||||
verify("createBox").withTimes(1)
|
||||
verify("putBox").withInOrder("1").withInOrder("2").withInOrder("3")
|
||||
verifyInvoked("createBox").withTimes(1)
|
||||
verifyInvoked("putBox").withInOrder("1").withInOrder("2").withInOrder("3")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ Besides `TestableMock`, there are also several other community mock tools, such
|
||||
|
||||
`JMockit` is a mock tool whose functionality and convenience are between `Mockito` and `PowerMock`, and it makes up for their respective shortcomings. The project tried to launch a rewritten version of JMockit2 in 2017 but failed to complete, and is currently in an inactive maintenance state.
|
||||
|
||||
The functionality of `TestabledMock` is basically the same as that of `PowerMock`, and it is extremely easy to use. You can complete most tasks only by mastering the annotations of `@MockMethod`.
|
||||
The functionality of `TestabledMock` is basically the same as that of `PowerMock`, and it is extremely easy to use. You can complete most tasks only by mastering the annotations of `@MockInvoke`.
|
||||
|
||||
The main disadvantage of the current `TestableMock` is that the IDE cannot promptly prompt whether the method parameters are matched correctly when writing the mock method. If the mocking effect does not meet expectation, it has to be verified during runtime through the method provided in the [self-help troubleshooting](en-us/doc/troubleshooting.md) document. This feature needs to be provided by extending IDE plugins in the future.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ The **verifiers** and **matchers** are provided in `TestableMock` to achieve thi
|
||||
@Test
|
||||
public test_case() {
|
||||
int res = insToTest.methodToTest();
|
||||
verify("mockMethod").with(123, "abc");
|
||||
verifyInvoked("mockMethod").with(123, "abc");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
# Release Note
|
||||
|
||||
## 0.7.0
|
||||
- fix an improper exception issue when mock method with `associated` scope invoked
|
||||
- rename type `InvokeVerifier`/`InvockeMatcher` to `InvocationVerifier`/`InvocationMatcher`
|
||||
- rename method `verify` in `InvocationVerifier` type to `verifyInvoked`
|
||||
- rename annotation `@MockMethod`/`@MockConstructor` to `@MockInvoke`/`@MockNew`
|
||||
|
||||
## 0.6.10
|
||||
- support mock invocation via function reference
|
||||
- support mock method defined in a base interface
|
||||
|
||||
## 0.6.9
|
||||
- support mock invocation in lambda method or via function reference
|
||||
- support mock invocation in lambda method
|
||||
- fix a `NullPointerException` issue when `PrivateAccessor.invoke()` has `null` parameter
|
||||
|
||||
## 0.6.8
|
||||
@@ -62,6 +72,13 @@
|
||||
- fix an exception caused by method parameter with ternary operator
|
||||
- fix a bug cause log message lost when `@MockWith` annotation used
|
||||
|
||||
## 0.5.0
|
||||
- split test class and mock class, let the mock class and method reusable
|
||||
- support use mock when package path of the test class is different from the class under test
|
||||
- support limit the mock effective scope to the cases of class under test it bound
|
||||
- use `TransmittableThreadLocal` to store mock context
|
||||
- add annotation `@MockDiagnose` to print debug logs
|
||||
|
||||
## 0.4.12
|
||||
- support verbose diagnose log for better self-troubleshooting
|
||||
- support disable private access target existence check
|
||||
|
||||
@@ -16,7 +16,7 @@ It is recommended to add a `property` field that identifies the TestableMock ver
|
||||
|
||||
```xml
|
||||
<properties>
|
||||
<testable.version>0.6.9</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
```
|
||||
|
||||
@@ -63,8 +63,8 @@ Add dependence of `TestableMock` in `build.gradle` file:
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.9')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.9')
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.10')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class Demo {
|
||||
|
||||
To test this method, you can use `TestableMock` to quickly mock out the `System.out.println` method. In the mock method body, you can simply call the original method (equivalent to not affecting the original method function, only used for call recording), or leave it blank (equivalent to removing the side effects of the original method).
|
||||
|
||||
After executing the void type method under test, use `InvokeVerifier.verify()` to verify whether the incoming print content meets expectations:
|
||||
After executing the void type method under test, use `InvocationVerifier.verifyInvoked()` to verify whether the incoming print content meets expectations:
|
||||
|
||||
```java
|
||||
class DemoTest {
|
||||
@@ -78,7 +78,7 @@ class DemoTest {
|
||||
|
||||
public static class Mock {
|
||||
// Intercept `System.out.println` invocation
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
public void println(PrintStream ps, String msg) {
|
||||
// Execute the original call
|
||||
ps.println(msg);
|
||||
@@ -90,7 +90,7 @@ class DemoTest {
|
||||
Action action = new Action("click", ":download");
|
||||
demo.recordAction();
|
||||
// Verify mock method `println` is invoked, and passing parameters in line with expectations
|
||||
verify("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\[click\\] :download"));
|
||||
verifyInvoked("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\[click\\] :download"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -39,7 +39,7 @@ public class DemoMockTest {
|
||||
@Test
|
||||
void should_mock_member_method() throws Exception {
|
||||
assertEquals("hello_world", demoMock.outerFunc());
|
||||
verify("innerFunc").with("world");
|
||||
verifyInvoked("innerFunc").with("world");
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -59,7 +59,7 @@ public class DemoMockTest {
|
||||
@Test
|
||||
void should_mock_member_method() throws Exception {
|
||||
assertEquals("hello_world", demoMock.outerFunc());
|
||||
verify("innerFunc").with("world");
|
||||
verifyInvoked("innerFunc").with("world");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,8 +6,8 @@ In unit testing, the main role of the mock method is to replace those methods wi
|
||||
Based on the above information, `TestableMock` has designed a minimalist mock mechanism. Unlike the common mock tools that uses **class** as the definition granularity of mocking, and repeats the description of mock behavior in each test case, `TestableMock` allows each business class (class under test) to be associated with a set of reusable collection of mock methods (carried by the mock container class), following the principle of "convention over configuration", and mock method replacement will automatically happen when the specified method in the test class match an invocation in the class under test.
|
||||
|
||||
> In summary, there are two simple rules:
|
||||
> - Mock non-constructive method, copy the original method definition to the mock class, add a `@MockMethod` annotation
|
||||
> - Mock construction method, copy the original method definition to the mock class, replace the return value with the constructed type, the method name is arbitrary, and add a `@MockContructor` annotation
|
||||
> - Mock non-constructive method, copy the original method definition to the mock class, add a `@MockInvoke` annotation
|
||||
> - Mock construction method, copy the original method definition to the mock class, replace the return value with the constructed type, the method name is arbitrary, and add a `@MockNew` annotation
|
||||
|
||||
The detail mock method definition convention is as follows.
|
||||
|
||||
@@ -27,7 +27,7 @@ public class DemoTest {
|
||||
|
||||
### 1.1 Mock method calls of any class
|
||||
|
||||
Define an ordinary method annotated with `@MockMethod` in the mock class with exactly the same signature (name, parameter, and return value type) as the method to be mocked, and then add the type of target object (which the method originally belongs to) as `targetMethod` parameter of `@MockMethod` annotation.
|
||||
Define an ordinary method annotated with `@MockInvoke` in the mock class with exactly the same signature (name, parameter, and return value type) as the method to be mocked, and then add the type of target object (which the method originally belongs to) as `targetMethod` parameter of `@MockInvoke` annotation.
|
||||
|
||||
At this time, all invocations to that original method in the class under test will be automatically replaced with invocations to the above-mentioned mock method when the unit test is running.
|
||||
|
||||
@@ -36,33 +36,33 @@ For example, there is a call to `"anything".substring(1, 2)` in the class under
|
||||
```java
|
||||
// The original method signature is `String substring(int, int)`
|
||||
// The object `"anything"` that invokes this method is of type `String`
|
||||
@MockMethod(targetClass = String.class)
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private String substring(int i, int j) {
|
||||
return "sub_string";
|
||||
}
|
||||
```
|
||||
|
||||
When several methods to be mocked have the same name, you can put the name of the method to be mocked in the `targetMethod` parameter of `@MockMethod` annotation, so that the mock method itself can be named at will.
|
||||
When several methods to be mocked have the same name, you can put the name of the method to be mocked in the `targetMethod` parameter of `@MockInvoke` annotation, so that the mock method itself can be named at will.
|
||||
|
||||
The following example shows the usage of the `targetMethod` parameter, and its effect is the same as the above example:
|
||||
|
||||
```java
|
||||
// Use `targetMethod` to specify the name of the method that needs to be mocked
|
||||
// The method itself can now be named arbitrarily, but the method parameters still need to follow the same matching rules
|
||||
@MockMethod(targetClass = String.class, targetMethod = "substring")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "substring")
|
||||
private String use_any_mock_method_name(int i, int j) {
|
||||
return "sub_string";
|
||||
}
|
||||
```
|
||||
|
||||
Sometimes, the mock method need to access the member variables in the original object that initiated the invocation, or invoke other methods of the original object. At this point, you can remove the `targetClass` parameter in the `@MockMethod` annotation, and then add a extra parameter whose type is the original object type of the method to the first index of the method parameter list.
|
||||
Sometimes, the mock method need to access the member variables in the original object that initiated the invocation, or invoke other methods of the original object. At this point, you can remove the `targetClass` parameter in the `@MockInvoke` annotation, and then add a extra parameter whose type is the original object type of the method to the first index of the method parameter list.
|
||||
|
||||
The `TestableMock` convention is that when the `targetClass` parameter of the `@MockMethod` annotation is not defined, the first parameter of the mock method is the type of the target method, and the parameter name is arbitrary. In order to facilitate code reading, it is recommended to name this parameter as `self` or `src`. Example as follows:
|
||||
The `TestableMock` convention is that when the `targetClass` parameter of the `@MockInvoke` annotation is not defined, the first parameter of the mock method is the type of the target method, and the parameter name is arbitrary. In order to facilitate code reading, it is recommended to name this parameter as `self` or `src`. Example as follows:
|
||||
|
||||
```java
|
||||
// Adds a `String` type parameter to the first position the mock method parameter list (parameter name is arbitrary)
|
||||
// This parameter can be used to get the value and context of the actual invoker at runtime
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private String substring(String self, int i, int j) {
|
||||
// Call the original method is also allowed
|
||||
return self.substring(i, j);
|
||||
@@ -81,7 +81,7 @@ For example, there is a private method with the signature `String innerFunc(Stri
|
||||
|
||||
```java
|
||||
// The type to test is `DemoMock`
|
||||
@MockMethod(targetClass = DemoMock.class)
|
||||
@MockInvoke(targetClass = DemoMock.class)
|
||||
private String innerFunc(String text) {
|
||||
return "mock_" + text;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ Mock for static methods is the same as for any ordinary methods.
|
||||
For example, if the static method `secretBox()` of the `BlackBox` type is invoked in the class under test, and the method signature is `BlackBox secretBox()`, then the mock method is as follows:
|
||||
|
||||
```java
|
||||
@MockMethod(targetClass = BlackBox.class)
|
||||
@MockInvoke(targetClass = BlackBox.class)
|
||||
private BlackBox secretBox() {
|
||||
return new BlackBox("not_secret_box");
|
||||
}
|
||||
@@ -108,7 +108,7 @@ For complete code examples, see the `should_mock_static_method()` test case in t
|
||||
|
||||
### 1.4 Mock `new` operation of any type
|
||||
|
||||
Define an ordinary method annotated with `@MockContructor` in the mock class, make the return value type of the method the type of the object to be created, and the method parameters are exactly the same as the constructor parameters to be mocked, the method name is arbitrary.
|
||||
Define an ordinary method annotated with `@MockNew` in the mock class, make the return value type of the method the type of the object to be created, and the method parameters are exactly the same as the constructor parameters to be mocked, the method name is arbitrary.
|
||||
|
||||
At this time, all operations in the class under test that use `new` to create the specified class (and use the constructor that is consistent with the mock method parameters) will be replaced with calls to the custom method.
|
||||
|
||||
@@ -117,13 +117,13 @@ For example, if there is a call to `new BlackBox("something")` in the class unde
|
||||
```java
|
||||
// The signature of the constructor to be mocked is `BlackBox(String)`
|
||||
// No need to add additional parameters to the mock method parameter list, and the name of the mock method is arbitrary
|
||||
@MockContructor
|
||||
@MockNew
|
||||
private BlackBox createBlackBox(String text) {
|
||||
return new BlackBox("mock_" + 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 `@MockInvoke` 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 `@MockNew` annotation
|
||||
|
||||
For complete code examples, see the `should_mock_new_object()` test case in the `java-demo` and `kotlin-demo` sample projects.
|
||||
|
||||
@@ -146,7 +146,7 @@ public void testDemo() {
|
||||
Take out the injected parameters in the mock method and return different results according to the situation:
|
||||
|
||||
```java
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private Data mockDemo() {
|
||||
switch((String)MOCK_CONTEXT.get("case")) {
|
||||
case "data-ready":
|
||||
@@ -163,7 +163,7 @@ For complete code examples, see the `should_get_source_method_name()` and `shoul
|
||||
|
||||
### 3. Verify the sequence and parameters of the mock method being invoked
|
||||
|
||||
In test cases, you can use the `InvokeVerifier.verify()` method, and cooperate with `with()`, `withInOrder()`, `without()`, `withTimes()` and other methods to verify the mock call situation.
|
||||
In test cases, you can use the `InvocationVerifier.verifyInvoked()` method, and cooperate with `with()`, `withInOrder()`, `without()`, `withTimes()` and other methods to verify the mock call situation.
|
||||
|
||||
For details, please refer to the [Check Mock Call](en-us/doc/matcher.md) document.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
| srcClass | Class | 否 | N/A | 当测试类命名不符合约定时,指定实际被测类 |
|
||||
| verifyTargetOnCompile | boolean | 否 | true | 是否启用私有目标的编译期存在性校验 |
|
||||
|
||||
#### @MockMethod
|
||||
#### @MockInvoke
|
||||
|
||||
将当前方法标识为待匹配的Mock成员方法。
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
| targetMethod | String | 否 | N/A | 指定Mock目标的方法名 |
|
||||
| scope | MockScope | 否 | MockScope.GLOBAL | 指定Mock的生效范围 |
|
||||
|
||||
#### @MockConstructor
|
||||
#### @MockNew
|
||||
|
||||
将当前方法标识为待匹配的Mock构造方法。
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
`JMockit`是一款功能性与易用性均居于`Mockito`与`PowerMock`之间的Mock工具,较好的弥补了两者各自的不足。该项目在2017年尝试推出JMockit2重写版本但未能完成,目前处于不活跃的维护状态。
|
||||
|
||||
相比之下,`TestabledMock`的功能与`PowerMock`基本平齐,且极易上手,只需掌握`@MockMethod`注解就可以完成绝大多数任务。
|
||||
相比之下,`TestabledMock`的功能与`PowerMock`基本平齐,且极易上手,只需掌握`@MockInvoke`注解就可以完成绝大多数任务。
|
||||
|
||||
当前`TestableMock`的主要不足在于,编写Mock方法时IDE无法即时提示方法参数是否正确匹配。若发现匹配效果不符合预期,需要通过[自助问题排查](zh-cn/doc/troubleshooting.md)文档提供的方法在运行期进行校验。这个功能理论上能够通过扩展主流IDE插件来补充,但目前暂无相关开发计划,参见[Issue-104](https://github.com/alibaba/testable-mock/issues/104)。
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@Test
|
||||
public test_case() {
|
||||
int res = insToTest.methodToTest();
|
||||
verify("mockMethod").with(123, "abc");
|
||||
verifyInvoked("mockMethod").with(123, "abc");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -59,4 +59,4 @@
|
||||
|
||||
若项目测试中,既包含真实的单元测试,又包含了使用单元测试框架编写的集成测试时。为了让集成测试的执行过程不受Mock影响,可能需要使用`mock.scope.default`将默认的Mock方法范围限制为仅对所属类型的单元测试用例生效。
|
||||
|
||||
若需Mock的调用发生在线程池中,且遇到`verify()`结果或`MOCK_CONTEXT`内容不正确的时候,则需考虑开启`thread.pool.enhance.enable`配置,详见[Mock线程池内的调用](zh-cn/doc/with-thread-pool.md)。
|
||||
若需Mock的调用发生在线程池中,且遇到`verifyInvoked()`结果或`MOCK_CONTEXT`内容不正确的时候,则需考虑开启`thread.pool.enhance.enable`配置,详见[Mock线程池内的调用](zh-cn/doc/with-thread-pool.md)。
|
||||
|
||||
@@ -51,7 +51,7 @@ public class BbbServiceTest {
|
||||
}
|
||||
|
||||
public class BasicMock {
|
||||
@MockMethod(targetClass = UserDao.class)
|
||||
@MockInvoke(targetClass = UserDao.class)
|
||||
protected String getById(int id) {
|
||||
...
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
# Release Note
|
||||
|
||||
## 0.7.0
|
||||
- 修复当`scope`为`associated`的Mock方法被`null`对象调用,且上下文与测试用例未关联时抛错不合理的问题(issue-163)
|
||||
- 类型`InvokeVerifier`和`InvockeMatcher`更名为`InvocationVerifier`和`InvocationMatcher`
|
||||
- 类型`InvocationVerifier`中的`verify`方法与`com.sun`包中的方法重名,更名为`verifyInvoked`
|
||||
- 注解`@MockMethod`和`@MockConstructor`更名为`@MockInvoke`和`@MockNew`
|
||||
|
||||
## 0.6.10
|
||||
- 支持Mock通过方法引用执行的调用(issue-233 / pr-234)
|
||||
- 支持Mock基类接口中的方法(pr-231)
|
||||
|
||||
## 0.6.9
|
||||
- 支持Lambda方法中的调用和方法引用(issue-36)
|
||||
- 支持Mock匿名方法体内部的方法调用(issue-36 / pr-208)
|
||||
- 修复`PrivateAccessor.invoke()`调用参数为`null`时的空指针异常(issue-226)
|
||||
|
||||
## 0.6.8
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
Mock的生效范围
|
||||
---
|
||||
|
||||
在`@MockMethod`和`@MockConstructor`注解上都有一个`scope`参数,其可选值有两种
|
||||
在`@MockInvoke`和`@MockNew`注解上都有一个`scope`参数,其可选值有两种
|
||||
|
||||
- `MockScope.GLOBAL`:该Mock方法将全局生效
|
||||
- `MockScope.ASSOCIATED`:该Mock方法仅对Mock容器关联测试类中的测试用例生效
|
||||
|
||||
对于常规项目而言,单元测试里需要被Mock的调用都是由于其中包含了不需要或不便于测试的逻辑,譬如“依赖外部系统”、“包含随机结果”、“执行非常耗时”等等,这类调用在整个单元测试的生命周期里都应该被Mock方法置换,不论调用的发起者是谁。因此`TestableMock`默认所有Mock方法都是全局生效的,即`scope`默认值为`MockScope.GLOBAL`。
|
||||
|
||||
> 举例来说,`CookerService`和`SellerService`是两个需要被测试的类,假设`CookerService`的代码里的`hireXxx()`和`cookXxx()`方法都需要依赖外部系统。因此在进行单元测试时,开发者在`CookerService`关联的Mock容器里使用`@MockMethod`注解定义了这些调用的替代方法。
|
||||
> 举例来说,`CookerService`和`SellerService`是两个需要被测试的类,假设`CookerService`的代码里的`hireXxx()`和`cookXxx()`方法都需要依赖外部系统。因此在进行单元测试时,开发者在`CookerService`关联的Mock容器里使用`@MockInvoke`注解定义了这些调用的替代方法。
|
||||
>
|
||||
> 此时,若该Mock方法的`scope`值为`MockScope.GLOBAL`,则不论是在`SellerServiceTest`测试类还是在`CookerServiceTest`测试类的测试用例,只要直接或间接的执行到这行调用,都会被置换为调用Mock方法。若该Mock方法的`scope`值为`MockScope.ASSOCIATED`,则Mock只对`CookerServiceTest`类中的测试用例生效,而`SellerServiceTest`类中的测试用例在运行过程中执行到了`CookerService`类的相关代码,将会执行原本的调用。
|
||||
>
|
||||
@@ -24,7 +24,7 @@ Mock的生效范围
|
||||
> ```
|
||||
> 若默认的`scope`参数不是`MockScope.GLOBAL`,则相应Mock方法应当显式的声明`scope`值,例如:
|
||||
> ```java
|
||||
> @MockMethod(targetClass = System.class, scope = MockScope.GLOBAL)
|
||||
> @MockInvoke(targetClass = System.class, scope = MockScope.GLOBAL)
|
||||
> private void loadLibrary(String libname) {
|
||||
> System.err.println("loadLibrary " + libname);
|
||||
> }
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
```xml
|
||||
<properties>
|
||||
<testable.version>0.6.9</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
```
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.9')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.9')
|
||||
testImplementation('com.alibaba.testable:testable-all:0.6.10')
|
||||
testAnnotationProcessor('com.alibaba.testable:testable-processor:0.6.10')
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class Demo {
|
||||
|
||||
若要测试此方法,可以利用`TestableMock`快速Mock掉`System.out.println`方法。在Mock方法体里可以继续执行原调用(相当于并不影响本来方法功能,仅用于做调用记录),也可以直接留空(相当于去除了原方法的副作用)。
|
||||
|
||||
在执行完被测的void类型方法以后,用`InvokeVerifier.verify()`校验传入的打印内容是否符合预期:
|
||||
在执行完被测的void类型方法以后,用`InvocationVerifier.verifyInvoked()`校验传入的打印内容是否符合预期:
|
||||
|
||||
```java
|
||||
class DemoTest {
|
||||
@@ -79,7 +79,7 @@ class DemoTest {
|
||||
|
||||
public static class Mock {
|
||||
// 拦截System.out.println调用
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
public void println(PrintStream ps, String msg) {
|
||||
// 执行原调用
|
||||
ps.println(msg);
|
||||
@@ -91,7 +91,7 @@ class DemoTest {
|
||||
Action action = new Action("click", ":download");
|
||||
demo.recordAction();
|
||||
// 验证Mock方法println被调用,且传入参数格式符合预期
|
||||
verify("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\[click\\] :download"));
|
||||
verifyInvoked("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\[click\\] :download"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -39,7 +39,7 @@ public class DemoMockTest {
|
||||
@Test
|
||||
void should_mock_member_method() throws Exception {
|
||||
assertEquals("hello_world", demoMock.outerFunc());
|
||||
verify("innerFunc").with("world");
|
||||
verifyInvoked("innerFunc").with("world");
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -59,7 +59,7 @@ public class DemoMockTest {
|
||||
@Test
|
||||
void should_mock_member_method() throws Exception {
|
||||
assertEquals("hello_world", demoMock.outerFunc());
|
||||
verify("innerFunc").with("world");
|
||||
verifyInvoked("innerFunc").with("world");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
基于上述特点,`TestableMock`设计了一种极简的Mock机制。与以往Mock工具以**类**作为Mock的定义粒度,在每个测试用例里各自重复描述Mock行为的方式不同,`TestableMock`让每个业务类(被测类)关联一组可复用的Mock方法集合(使用Mock容器类承载),并遵循约定优于配置的原则,按照规则自动在测试运行时替换被测类中的指定方法调用。
|
||||
|
||||
> 实际规则约定归纳起来只有两条:
|
||||
> - Mock非构造方法,拷贝原方法定义到Mock容器类,加`@MockMethod`注解
|
||||
> - Mock构造方法,拷贝原方法定义到Mock容器类,返回值换成构造的类型,方法名随意,加`@MockContructor`注解
|
||||
> - Mock非构造方法,拷贝原方法定义到Mock容器类,加`@MockInvoke`注解
|
||||
> - Mock构造方法,拷贝原方法定义到Mock容器类,返回值换成构造的类型,方法名随意,加`@MockNew`注解
|
||||
|
||||
具体使用方法如下。
|
||||
|
||||
@@ -27,7 +27,7 @@ public class DemoTest {
|
||||
|
||||
### 1.1 覆写任意类的方法调用
|
||||
|
||||
在Mock容器类中定义一个有`@MockMethod`注解的普通方法,使它与需覆写的方法名称、参数、返回值类型完全一致,并在注解的`targetClass`参数指定该方法原本所属对象类型。
|
||||
在Mock容器类中定义一个有`@MockInvoke`注解的普通方法,使它与需覆写的方法名称、参数、返回值类型完全一致,并在注解的`targetClass`参数指定该方法原本所属对象类型。
|
||||
|
||||
此时被测类中所有对该需覆写方法的调用,将在单元测试运行时,将自动被替换为对上述自定义Mock方法的调用。
|
||||
|
||||
@@ -36,33 +36,33 @@ public class DemoTest {
|
||||
```java
|
||||
// 原方法签名为`String substring(int, int)`
|
||||
// 调用此方法的对象`"something"`类型为`String`
|
||||
@MockMethod(targetClass = String.class)
|
||||
@MockInvoke(targetClass = String.class)
|
||||
private String substring(int i, int j) {
|
||||
return "sub_string";
|
||||
}
|
||||
```
|
||||
|
||||
当遇到待覆写方法有重名时,可以将需覆写的方法名写到`@MockMethod`注解的`targetMethod`参数里,这样Mock方法自身就可以随意命名了。
|
||||
当遇到待覆写方法有重名时,可以将需覆写的方法名写到`@MockInvoke`注解的`targetMethod`参数里,这样Mock方法自身就可以随意命名了。
|
||||
|
||||
下面这个例子展示了`targetMethod`参数的用法,其效果与上述示例相同:
|
||||
|
||||
```java
|
||||
// 使用`targetMethod`指定需Mock的方法名
|
||||
// 此方法本身现在可以随意命名,但方法参数依然需要遵循相同的匹配规则
|
||||
@MockMethod(targetClass = String.class, targetMethod = "substring")
|
||||
@MockInvoke(targetClass = String.class, targetMethod = "substring")
|
||||
private String use_any_mock_method_name(int i, int j) {
|
||||
return "sub_string";
|
||||
}
|
||||
```
|
||||
|
||||
有时,在Mock方法里会需要访问发起调用的原始对象中的成员变量,或是调用原始对象的其他方法。此时,可以将`@MockMethod`注解中的`targetClass`参数去除,然后在方法参数列表首位增加一个类型为该方法原本所属对象类型的参数。
|
||||
有时,在Mock方法里会需要访问发起调用的原始对象中的成员变量,或是调用原始对象的其他方法。此时,可以将`@MockInvoke`注解中的`targetClass`参数去除,然后在方法参数列表首位增加一个类型为该方法原本所属对象类型的参数。
|
||||
|
||||
`TestableMock`约定,当`@MockMethod`注解的`targetClass`参数未定义时,Mock方法的首位参数即为目标方法所属类型,参数名称随意。通常为了便于代码阅读,建议将此参数统一命名为`self`或`src`。举例如下:
|
||||
`TestableMock`约定,当`@MockInvoke`注解的`targetClass`参数未定义时,Mock方法的首位参数即为目标方法所属类型,参数名称随意。通常为了便于代码阅读,建议将此参数统一命名为`self`或`src`。举例如下:
|
||||
|
||||
```java
|
||||
// Mock方法在参数列表首位增加一个类型为`String`的参数(名字随意)
|
||||
// 此参数可用于获得当时的实际调用者的值和上下文
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private String substring(String self, int i, int j) {
|
||||
// 可以直接调用原方法,此时Mock方法仅用于记录调用,常见于对void方法的测试
|
||||
return self.substring(i, j);
|
||||
@@ -81,7 +81,7 @@ private String substring(String self, int i, int j) {
|
||||
|
||||
```java
|
||||
// 被测类型是`DemoMock`
|
||||
@MockMethod(targetClass = DemoMock.class)
|
||||
@MockInvoke(targetClass = DemoMock.class)
|
||||
private String innerFunc(String text) {
|
||||
return "mock_" + text;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ private String innerFunc(String text) {
|
||||
例如,在被测类中调用了`BlackBox`类型中的静态方法`secretBox()`,该方法签名为`BlackBox secretBox()`,则Mock方法如下:
|
||||
|
||||
```java
|
||||
@MockMethod(targetClass = BlackBox.class)
|
||||
@MockInvoke(targetClass = BlackBox.class)
|
||||
private BlackBox secretBox() {
|
||||
return new BlackBox("not_secret_box");
|
||||
}
|
||||
@@ -110,7 +110,7 @@ private BlackBox secretBox() {
|
||||
|
||||
### 1.4 覆写任意类的new操作
|
||||
|
||||
在Mock容器类里定义一个返回值类型为要被创建的对象类型,且方法参数与要Mock的构造函数参数完全一致的方法,名称随意,然后加上`@MockContructor`注解。
|
||||
在Mock容器类里定义一个返回值类型为要被创建的对象类型,且方法参数与要Mock的构造函数参数完全一致的方法,名称随意,然后加上`@MockNew`注解。
|
||||
|
||||
此时被测类中所有用`new`创建指定类的操作(并使用了与Mock方法参数一致的构造函数)将被替换为对该自定义方法的调用。
|
||||
|
||||
@@ -119,7 +119,7 @@ private BlackBox secretBox() {
|
||||
```java
|
||||
// 要覆写的构造函数签名为`BlackBox(String)`
|
||||
// Mock方法返回`BlackBox`类型对象,方法的名称随意起
|
||||
@MockContructor
|
||||
@MockNew
|
||||
private BlackBox createBlackBox(String text) {
|
||||
return new BlackBox("mock_" + text);
|
||||
}
|
||||
@@ -146,7 +146,7 @@ public void testDemo() {
|
||||
在Mock方法中取出注入的参数,根据情况返回不同结果:
|
||||
|
||||
```java
|
||||
@MockMethod
|
||||
@MockInvoke
|
||||
private Data mockDemo() {
|
||||
switch((String)MOCK_CONTEXT.get("case")) {
|
||||
case "data-ready":
|
||||
@@ -163,7 +163,7 @@ private Data mockDemo() {
|
||||
|
||||
### 3. 验证Mock方法被调用的顺序和参数
|
||||
|
||||
在测试用例中可用通过`InvokeVerifier.verify()`方法,配合`with()`、`withInOrder()`、`without()`、`withTimes()`等方法实现对Mock调用情况的验证。
|
||||
在测试用例中可用通过`InvocationVerifier.verifyInvoked()`方法,配合`with()`、`withInOrder()`、`without()`、`withTimes()`等方法实现对Mock调用情况的验证。
|
||||
|
||||
详见[校验Mock调用](zh-cn/doc/matcher.md)文档。
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Mock线程池内的调用
|
||||
|
||||
`TestableMock`采用来自[transmittable-thread-local](https://github.com/alibaba/transmittable-thread-local)项目的`TransmittableThreadLocal`类型存储测试用例运行期的`MOCK_CONTEXT`内容和Mock方法调用过程。
|
||||
|
||||
当线程池中的执行对象未经过`TtlRunnable`或`TtlCallable`处理时,`TransmittableThreadLocal`将自动降级为与`InheritableThreadLocal`等效的类型,即只对父子线程有效,无法在线程池上下文中正常传递存储数据。因而会导致`MOCK_CONTEXT`内容丢失和`verify()`方法校验结果不正确的情况。
|
||||
当线程池中的执行对象未经过`TtlRunnable`或`TtlCallable`处理时,`TransmittableThreadLocal`将自动降级为与`InheritableThreadLocal`等效的类型,即只对父子线程有效,无法在线程池上下文中正常传递存储数据。因而会导致`MOCK_CONTEXT`内容丢失和`verifyInvoked()`方法校验结果不正确的情况。
|
||||
|
||||
为此,可以启用[Testable全局配置](zh-cn/doc/javaagent-args.md)`thread.pool.enhance.enable=true`,来自动在测试启动时自动封装程序中的普通`Runnable`和`Callable`对象,使`TransmittableThreadLocal`恢复跨线程池存储数据的能力。
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.testable</groupId>
|
||||
<artifactId>testable-parent</artifactId>
|
||||
<version>0.6.9</version>
|
||||
<version>0.6.10</version>
|
||||
<relativePath>../testable-parent</relativePath>
|
||||
</parent>
|
||||
<artifactId>testable-agent</artifactId>
|
||||
|
||||
@@ -16,8 +16,8 @@ public class ConstPool {
|
||||
public static final String MOCK_WITH = "com.alibaba.testable.core.annotation.MockWith";
|
||||
public static final String DUMP_TO = "com.alibaba.testable.core.annotation.DumpTo";
|
||||
public static final String MOCK_DIAGNOSE = "com.alibaba.testable.core.annotation.MockDiagnose";
|
||||
public static final String MOCK_METHOD = "com.alibaba.testable.core.annotation.MockMethod";
|
||||
public static final String MOCK_CONSTRUCTOR = "com.alibaba.testable.core.annotation.MockConstructor";
|
||||
public static final String MOCK_INVOKE = "com.alibaba.testable.core.annotation.MockInvoke";
|
||||
public static final String MOCK_NEW = "com.alibaba.testable.core.annotation.MockNew";
|
||||
|
||||
public static final String CGLIB_CLASS_PATTERN = "$$EnhancerBy";
|
||||
public static final String KOTLIN_POSTFIX_COMPANION = "$Companion";
|
||||
|
||||
@@ -93,12 +93,12 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* put targetClass parameter in @MockMethod to first parameter of the mock method
|
||||
* put targetClass parameter in @MockInvoke to first parameter of the mock method
|
||||
*/
|
||||
private void unfoldTargetClass(MethodNode mn) {
|
||||
String targetClassName = null;
|
||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||
if (ClassUtil.toByteCodeClassName(ConstPool.MOCK_METHOD).equals(an.desc)) {
|
||||
if (ClassUtil.toByteCodeClassName(ConstPool.MOCK_INVOKE).equals(an.desc)) {
|
||||
Type type = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_CLASS, null, Type.class);
|
||||
if (type != null) {
|
||||
targetClassName = ClassUtil.toByteCodeClassName(type.getClassName());
|
||||
@@ -212,7 +212,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
if (name != null) {
|
||||
methodName = name;
|
||||
}
|
||||
} else if (isMockConstructorAnnotation(an)) {
|
||||
} else if (isMockNewAnnotation(an)) {
|
||||
methodName = CONSTRUCTOR;
|
||||
}
|
||||
}
|
||||
@@ -226,7 +226,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
|
||||
private boolean isGlobalScope(MethodNode mn) {
|
||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||
if (isMockMethodAnnotation(an) || isMockConstructorAnnotation(an)) {
|
||||
if (isMockMethodAnnotation(an) || isMockNewAnnotation(an)) {
|
||||
MockScope scope = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_SCOPE,
|
||||
GlobalConfig.defaultMockScope, MockScope.class);
|
||||
if (scope.equals(MockScope.GLOBAL)) {
|
||||
@@ -248,7 +248,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
getTargetMethodOwner(mn, an), getTargetMethodName(mn, an), getTargetMethodDesc(mn, an)));
|
||||
}
|
||||
return true;
|
||||
} else if (isMockConstructorAnnotation(an)) {
|
||||
} else if (isMockNewAnnotation(an)) {
|
||||
if (LogUtil.isVerboseEnabled()) {
|
||||
LogUtil.verbose(" Mock constructor \"%s\" as \"%s\"", mn.name, MethodUtil.toJavaMethodDesc(
|
||||
ClassUtil.toJavaStyleClassName(MethodUtil.getReturnType(mn.desc)), mn.desc));
|
||||
@@ -277,12 +277,12 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
return type == null ? MethodUtil.removeFirstParameter(mn.desc) : mn.desc;
|
||||
}
|
||||
|
||||
private boolean isMockConstructorAnnotation(AnnotationNode an) {
|
||||
return ClassUtil.toByteCodeClassName(ConstPool.MOCK_CONSTRUCTOR).equals(an.desc);
|
||||
private boolean isMockNewAnnotation(AnnotationNode an) {
|
||||
return ClassUtil.toByteCodeClassName(ConstPool.MOCK_NEW).equals(an.desc);
|
||||
}
|
||||
|
||||
private boolean isMockMethodAnnotation(AnnotationNode an) {
|
||||
return ClassUtil.toByteCodeClassName(ConstPool.MOCK_METHOD).equals(an.desc);
|
||||
return ClassUtil.toByteCodeClassName(ConstPool.MOCK_INVOKE).equals(an.desc);
|
||||
}
|
||||
|
||||
private void injectInvokeRecorder(MethodNode mn) {
|
||||
@@ -324,9 +324,9 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
private boolean isMockForConstructor(MethodNode mn) {
|
||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||
String annotationName = ClassUtil.toJavaStyleClassName(an.desc);
|
||||
if (ConstPool.MOCK_CONSTRUCTOR.equals(annotationName)) {
|
||||
if (ConstPool.MOCK_NEW.equals(annotationName)) {
|
||||
return true;
|
||||
} else if (ConstPool.MOCK_METHOD.equals(annotationName)) {
|
||||
} else if (ConstPool.MOCK_INVOKE.equals(annotationName)) {
|
||||
String method = AnnotationUtil.getAnnotationParameter
|
||||
(an, ConstPool.FIELD_TARGET_METHOD, null, String.class);
|
||||
if (CONSTRUCTOR.equals(method)) {
|
||||
|
||||
@@ -139,7 +139,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
for (MethodInfo m : memberInjectMethods) {
|
||||
String nodeOwner = ClassUtil.fitCompanionClassName(node.owner);
|
||||
String nodeName = ClassUtil.fitKotlinAccessorName(node.name);
|
||||
// Kotlin accessor method will append a extra type parameter
|
||||
// Kotlin accessor method will append an extra type parameter
|
||||
String nodeDesc = nodeName.equals(node.name) ? node.desc : MethodUtil.removeFirstParameter(node.desc);
|
||||
if (m.getClazz().equals(nodeOwner) && m.getName().equals(nodeName) && m.getDesc().equals(nodeDesc)) {
|
||||
return m;
|
||||
@@ -308,7 +308,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
mn.instructions.remove(instructions[end - 1]);
|
||||
}
|
||||
}
|
||||
// method with @MockMethod will be modified as public access
|
||||
// method with @MockInvoke will be modified as public access
|
||||
int invokeOpcode = mockMethod.isStatic() ? INVOKESTATIC : INVOKEVIRTUAL;
|
||||
mn.instructions.insertBefore(instructions[end], new MethodInsnNode(invokeOpcode, mockClassName,
|
||||
mockMethod.getMockName(), mockMethod.getMockDesc(), false));
|
||||
@@ -369,6 +369,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
|
||||
// process for method reference
|
||||
for (Handle handle : invokeDynamicList) {
|
||||
// the jdk auto generation method
|
||||
if (handle.getName().startsWith("lambda$")) {
|
||||
continue;
|
||||
}
|
||||
@@ -379,7 +380,15 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
// lambda new method reference
|
||||
continue;
|
||||
}
|
||||
boolean isStatic = tag == Opcodes.H_INVOKESTATIC;
|
||||
|
||||
// external mean:
|
||||
// public void foo() {
|
||||
// String s = "";
|
||||
// consumes(s::contains);
|
||||
//}
|
||||
boolean external = tag == Opcodes.H_INVOKEVIRTUAL;
|
||||
|
||||
boolean isStatic = tag == Opcodes.H_INVOKESTATIC || external;
|
||||
|
||||
String desc = handle.getDesc();
|
||||
String parameters = desc.substring(desc.indexOf("(") + 1, desc.lastIndexOf(")"));
|
||||
@@ -391,8 +400,15 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
len = external ? len + 1 : len;
|
||||
|
||||
String[] refineParameterArray = new String[len];
|
||||
int index = 0;
|
||||
int index = external ? 1 : 0;
|
||||
if (external) {
|
||||
// The type should was reference type
|
||||
refineParameterArray[0] = "L" + handle.getOwner();
|
||||
}
|
||||
for (String s : parameterArray) {
|
||||
if (!s.isEmpty()) {
|
||||
refineParameterArray[index] = s;
|
||||
@@ -400,14 +416,16 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
}
|
||||
}
|
||||
|
||||
String externalDesc = buildDesc(refineParameterArray, returnType);
|
||||
|
||||
String lambdaName = String.format("Lambda$_%s_%d", handle.getName(), atomicInteger.incrementAndGet());
|
||||
MethodVisitor mv = cn.visitMethod(isStatic ? ACC_PUBLIC + ACC_STATIC : ACC_PUBLIC, lambdaName, desc, null, null);
|
||||
|
||||
|
||||
MethodVisitor mv = cn.visitMethod(isStatic ? ACC_PUBLIC + ACC_STATIC : ACC_PUBLIC, lambdaName, external ? externalDesc : desc, null, null);
|
||||
mv.visitCode();
|
||||
|
||||
Label l0 = new Label();
|
||||
mv.visitLabel(l0);
|
||||
if (!isStatic) {
|
||||
// add this
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
}
|
||||
for (int i = 0; i < refineParameterArray.length; i++) {
|
||||
@@ -415,19 +433,19 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
mv.visitVarInsn(getLoadType(arg), isStatic ? i : i + 1);
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(isStatic ? INVOKESTATIC : INVOKEVIRTUAL/*INVOKESPECIAL*/, handle.getOwner(), handle.getName(), desc, false);
|
||||
mv.visitMethodInsn(isStatic ? INVOKESTATIC : INVOKEVIRTUAL, handle.getOwner(), handle.getName(), desc, false);
|
||||
|
||||
mv.visitInsn(getReturnType(returnType));
|
||||
|
||||
Label l1 = new Label();
|
||||
mv.visitLabel(l1);
|
||||
|
||||
String localVarOwner = handle.getOwner();
|
||||
|
||||
// static function was not required add this to first parameter
|
||||
if (isStatic) {
|
||||
for (int i = 0; i < refineParameterArray.length; i++) {
|
||||
String localVar = refineParameterArray[i];
|
||||
if (!isPrimitive(localVar)) {
|
||||
// primitive type and reference type difference
|
||||
localVar = localVar.endsWith(";") ? localVar : localVar + ";";
|
||||
}
|
||||
|
||||
@@ -435,10 +453,11 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
continue;
|
||||
}
|
||||
|
||||
// add local var
|
||||
mv.visitLocalVariable(String.format("o%d", i), localVar, null, l0, l1, i);
|
||||
}
|
||||
} else {
|
||||
mv.visitLocalVariable("this", "L" + localVarOwner + ";", null, l0, l1, 0);
|
||||
mv.visitLocalVariable("this", "L" + handle.getOwner() + ";", null, l0, l1, 0);
|
||||
for (int i = 0; i < refineParameterArray.length; i++) {
|
||||
String localVar = refineParameterArray[i];
|
||||
if (!isPrimitive(localVar) && !isPrimitiveArray(localVar)) {
|
||||
@@ -456,15 +475,36 @@ public class SourceClassHandler extends BaseClassHandler {
|
||||
mv.visitEnd();
|
||||
|
||||
try {
|
||||
// modify handle to the generation method
|
||||
setFinalValue(handle.getClass().getDeclaredField("name"), handle, lambdaName);
|
||||
// mark: should merge the below two if.
|
||||
if (!handle.getOwner().equals(cn.name) && isStatic) {
|
||||
setFinalValue(handle.getClass().getDeclaredField("owner"), handle, cn.name);
|
||||
}
|
||||
if (external) {
|
||||
setFinalValue(handle.getClass().getDeclaredField("owner"), handle, cn.name);
|
||||
setFinalValue(handle.getClass().getDeclaredField("descriptor"), handle, externalDesc);
|
||||
setFinalValue(handle.getClass().getDeclaredField("tag"), handle, H_INVOKESTATIC);
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildDesc(String[] refineParameterArray, String returnType) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (String s : refineParameterArray) {
|
||||
sb.append(s);
|
||||
if (!isPrimitive(s)) {
|
||||
sb.append(";");
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
sb.append(returnType);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean isPrimitive(String type) {
|
||||
if (type.endsWith(";")) {
|
||||
|
||||
@@ -57,8 +57,8 @@ public class MockClassParser {
|
||||
if (mn.visibleAnnotations != null) {
|
||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||
String fullClassName = toJavaStyleClassName(an.desc);
|
||||
if (fullClassName.equals(ConstPool.MOCK_METHOD) ||
|
||||
fullClassName.equals(ConstPool.MOCK_CONSTRUCTOR)) {
|
||||
if (fullClassName.equals(ConstPool.MOCK_INVOKE) ||
|
||||
fullClassName.equals(ConstPool.MOCK_NEW)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,12 @@ public class MockClassParser {
|
||||
mns.addAll(getAllMethods(scn));
|
||||
}
|
||||
}
|
||||
for(String interfaceClass : cn.interfaces) {
|
||||
ClassNode scn = ClassUtil.getClassNode(interfaceClass);
|
||||
if (scn != null) {
|
||||
mns.addAll(getAllMethods(scn));
|
||||
}
|
||||
}
|
||||
for (InnerClassNode innerClass : cn.innerClasses) {
|
||||
if (innerClass.name.equals(cn.name + KOTLIN_POSTFIX_COMPANION)) {
|
||||
ClassNode scn = ClassUtil.getClassNode(innerClass.name);
|
||||
@@ -92,13 +98,13 @@ public class MockClassParser {
|
||||
}
|
||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||
String fullClassName = toJavaStyleClassName(an.desc);
|
||||
if (fullClassName.equals(ConstPool.MOCK_CONSTRUCTOR)) {
|
||||
if (fullClassName.equals(ConstPool.MOCK_NEW)) {
|
||||
if (GlobalConfig.checkMockTargetExistence) {
|
||||
checkTargetConstructorExists(cn, mn);
|
||||
}
|
||||
methodInfos.add(new MethodInfo(ClassUtil.getSourceClassName(cn.name), CONSTRUCTOR, mn.desc, cn.name,
|
||||
mn.name, mn.desc, isStatic(mn)));
|
||||
} else if (fullClassName.equals(ConstPool.MOCK_METHOD) && isValidMockMethod(mn, an)) {
|
||||
} else if (fullClassName.equals(ConstPool.MOCK_INVOKE) && isValidMockMethod(mn, an)) {
|
||||
if (GlobalConfig.checkMockTargetExistence) {
|
||||
checkTargetMethodExists(cn, mn, an);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.testable</groupId>
|
||||
<artifactId>testable-parent</artifactId>
|
||||
<version>0.6.9</version>
|
||||
<version>0.6.10</version>
|
||||
<relativePath>../testable-parent</relativePath>
|
||||
</parent>
|
||||
<artifactId>testable-all</artifactId>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.testable</groupId>
|
||||
<artifactId>testable-parent</artifactId>
|
||||
<version>0.6.9</version>
|
||||
<version>0.6.10</version>
|
||||
<relativePath>../testable-parent</relativePath>
|
||||
</parent>
|
||||
<artifactId>testable-core</artifactId>
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface MockMethod {
|
||||
public @interface MockInvoke {
|
||||
|
||||
/**
|
||||
* mock specified method instead of method with same name
|
||||
@@ -12,7 +12,7 @@ import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface MockConstructor {
|
||||
public @interface MockNew {
|
||||
|
||||
/**
|
||||
* specify the effective scope of the mock method
|
||||
@@ -10,19 +10,19 @@ import java.util.Set;
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class InvokeMatcher {
|
||||
public class InvocationMatcher {
|
||||
|
||||
public MatchFunction matchFunction;
|
||||
|
||||
private InvokeMatcher(MatchFunction matchFunction) {
|
||||
private InvocationMatcher(MatchFunction matchFunction) {
|
||||
this.matchFunction = matchFunction;
|
||||
}
|
||||
|
||||
public static InvokeMatcher any(MatchFunction matcher) {
|
||||
return new InvokeMatcher(matcher);
|
||||
public static InvocationMatcher any(MatchFunction matcher) {
|
||||
return new InvocationMatcher(matcher);
|
||||
}
|
||||
|
||||
public static InvokeMatcher any() {
|
||||
public static InvocationMatcher any() {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -31,47 +31,47 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyString() {
|
||||
public static InvocationMatcher anyString() {
|
||||
return any(String.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyNumber() {
|
||||
public static InvocationMatcher anyNumber() {
|
||||
return anyTypeOf(Short.class, Integer.class, Long.class, Float.class, Double.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyBoolean() {
|
||||
public static InvocationMatcher anyBoolean() {
|
||||
return any(Boolean.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyByte() {
|
||||
public static InvocationMatcher anyByte() {
|
||||
return any(Byte.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyChar() {
|
||||
public static InvocationMatcher anyChar() {
|
||||
return any(Character.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyInt() {
|
||||
public static InvocationMatcher anyInt() {
|
||||
return any(Integer.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyLong() {
|
||||
public static InvocationMatcher anyLong() {
|
||||
return any(Long.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyFloat() {
|
||||
public static InvocationMatcher anyFloat() {
|
||||
return any(Float.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyDouble() {
|
||||
public static InvocationMatcher anyDouble() {
|
||||
return any(Double.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyShort() {
|
||||
public static InvocationMatcher anyShort() {
|
||||
return any(Short.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyArray() {
|
||||
public static InvocationMatcher anyArray() {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -81,7 +81,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyArrayOf(final Class<?> clazz) {
|
||||
public static InvocationMatcher anyArrayOf(final Class<?> clazz) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -92,47 +92,47 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyList() {
|
||||
public static InvocationMatcher anyList() {
|
||||
return any(List.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyListOf(final Class<?> clazz) {
|
||||
public static InvocationMatcher anyListOf(final Class<?> clazz) {
|
||||
return anyClassWithCollectionOf(List.class, clazz);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anySet() {
|
||||
public static InvocationMatcher anySet() {
|
||||
return any(Set.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anySetOf(final Class<?> clazz) {
|
||||
public static InvocationMatcher anySetOf(final Class<?> clazz) {
|
||||
return anyClassWithCollectionOf(Set.class, clazz);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyMap() {
|
||||
public static InvocationMatcher anyMap() {
|
||||
return any(Map.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyMapOf(final Class<?> keyClass, final Class<?> valueClass) {
|
||||
public static InvocationMatcher anyMapOf(final Class<?> keyClass, final Class<?> valueClass) {
|
||||
return anyClassWithMapOf(keyClass, valueClass);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyCollection() {
|
||||
public static InvocationMatcher anyCollection() {
|
||||
return any(Collection.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyCollectionOf(final Class<?> clazz) {
|
||||
public static InvocationMatcher anyCollectionOf(final Class<?> clazz) {
|
||||
return anyClassWithCollectionOf(Collection.class, clazz);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyIterable() {
|
||||
public static InvocationMatcher anyIterable() {
|
||||
return any(Iterable.class);
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyIterableOf(final Class<?> clazz) {
|
||||
public static InvocationMatcher anyIterableOf(final Class<?> clazz) {
|
||||
return anyClassWithCollectionOf(Iterable.class, clazz);
|
||||
}
|
||||
|
||||
public static InvokeMatcher any(final Class<?> clazz) {
|
||||
public static InvocationMatcher any(final Class<?> clazz) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -141,7 +141,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher anyTypeOf(final Class<?>... classes) {
|
||||
public static InvocationMatcher anyTypeOf(final Class<?>... classes) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -158,7 +158,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher eq(final Object obj) {
|
||||
public static InvocationMatcher eq(final Object obj) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -167,7 +167,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher refEq(final Object obj) {
|
||||
public static InvocationMatcher refEq(final Object obj) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -176,7 +176,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher isNull() {
|
||||
public static InvocationMatcher isNull() {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -185,7 +185,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher notNull() {
|
||||
public static InvocationMatcher notNull() {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -194,7 +194,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher nullable(final Class<?> clazz) {
|
||||
public static InvocationMatcher nullable(final Class<?> clazz) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -203,7 +203,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher contains(final String substring) {
|
||||
public static InvocationMatcher contains(final String substring) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -212,7 +212,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher matches(final String regex) {
|
||||
public static InvocationMatcher matches(final String regex) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -221,7 +221,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher endsWith(final String suffix) {
|
||||
public static InvocationMatcher endsWith(final String suffix) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -230,7 +230,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static InvokeMatcher startsWith(final String prefix) {
|
||||
public static InvocationMatcher startsWith(final String prefix) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -239,7 +239,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
private static InvokeMatcher anyClassWithCollectionOf(final Class<?> collectionClass, final Class<?> clazz) {
|
||||
private static InvocationMatcher anyClassWithCollectionOf(final Class<?> collectionClass, final Class<?> clazz) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -250,7 +250,7 @@ public class InvokeMatcher {
|
||||
});
|
||||
}
|
||||
|
||||
private static InvokeMatcher anyClassWithMapOf(final Class<?> keyClass, final Class<?> valueClass) {
|
||||
private static InvocationMatcher anyClassWithMapOf(final Class<?> keyClass, final Class<?> valueClass) {
|
||||
return any(new MatchFunction() {
|
||||
@Override
|
||||
public boolean check(Object value) {
|
||||
@@ -11,12 +11,12 @@ import java.util.List;
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class InvokeVerifier {
|
||||
public class InvocationVerifier {
|
||||
|
||||
private final List<Object[]> records;
|
||||
private Verification lastVerification = null;
|
||||
|
||||
private InvokeVerifier(List<Object[]> records) {
|
||||
private InvocationVerifier(List<Object[]> records) {
|
||||
this.records = records;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ public class InvokeVerifier {
|
||||
* @param mockMethodName name of a mock method
|
||||
* @return the verifier object
|
||||
*/
|
||||
public static InvokeVerifier verify(String mockMethodName) {
|
||||
return new InvokeVerifier(MockContextUtil.context.get().invokeRecord.get(mockMethodName));
|
||||
public static InvocationVerifier verifyInvoked(String mockMethodName) {
|
||||
return new InvocationVerifier(MockContextUtil.context.get().invokeRecord.get(mockMethodName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,7 +34,7 @@ public class InvokeVerifier {
|
||||
* @param args parameters to compare
|
||||
* @return the verifier object
|
||||
*/
|
||||
public InvokeVerifier with(Object... args) {
|
||||
public InvocationVerifier with(Object... args) {
|
||||
boolean found = false;
|
||||
for (int i = 0; i < records.size(); i++) {
|
||||
try {
|
||||
@@ -57,7 +57,7 @@ public class InvokeVerifier {
|
||||
* @param args parameters to compare
|
||||
* @return the verifier object
|
||||
*/
|
||||
public InvokeVerifier withInOrder(Object... args) {
|
||||
public InvocationVerifier withInOrder(Object... args) {
|
||||
withInternal(args, 0);
|
||||
lastVerification = new Verification(args, true);
|
||||
return this;
|
||||
@@ -68,7 +68,7 @@ public class InvokeVerifier {
|
||||
* @param args parameters to compare
|
||||
* @return the verifier object
|
||||
*/
|
||||
public InvokeVerifier without(Object... args) {
|
||||
public InvocationVerifier without(Object... args) {
|
||||
for (Object[] r : records) {
|
||||
if (r.length == args.length) {
|
||||
for (int i = 0; i < r.length; i++) {
|
||||
@@ -90,7 +90,7 @@ public class InvokeVerifier {
|
||||
* @param expectedCount times to compare
|
||||
* @return the verifier object
|
||||
*/
|
||||
public InvokeVerifier withTimes(int expectedCount) {
|
||||
public InvocationVerifier withTimes(int expectedCount) {
|
||||
if (expectedCount != records.size()) {
|
||||
throw new VerifyFailedError("times: " + expectedCount, "times: " + records.size());
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public class InvokeVerifier {
|
||||
* @param count number of invocations
|
||||
* @return the verifier object
|
||||
*/
|
||||
public InvokeVerifier times(int count) {
|
||||
public InvocationVerifier times(int count) {
|
||||
if (lastVerification == null) {
|
||||
// when used independently, equals to `withTimes()`
|
||||
System.out.println("Warning: [" + TestableUtil.previousStackLocation() + "] using \"times()\" method "
|
||||
@@ -133,7 +133,7 @@ public class InvokeVerifier {
|
||||
throw new VerifyFailedError(desc(args), desc(record));
|
||||
}
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (!(args[i] instanceof InvokeMatcher || args[i].getClass().equals(record[i].getClass()))) {
|
||||
if (!(args[i] instanceof InvocationMatcher || args[i].getClass().equals(record[i].getClass()))) {
|
||||
throw new VerifyFailedError("parameter " + (i + 1) + " type mismatch",
|
||||
": " + args[i].getClass(), ": " + record[i].getClass());
|
||||
}
|
||||
@@ -145,8 +145,8 @@ public class InvokeVerifier {
|
||||
}
|
||||
|
||||
private boolean matches(Object expectValue, Object realValue) {
|
||||
return expectValue instanceof InvokeMatcher ?
|
||||
((InvokeMatcher) expectValue).matchFunction.check(realValue) :
|
||||
return expectValue instanceof InvocationMatcher ?
|
||||
((InvocationMatcher) expectValue).matchFunction.check(realValue) :
|
||||
expectValue.equals(realValue);
|
||||
}
|
||||
|
||||
@@ -189,13 +189,13 @@ public class PrivateAccessor {
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MemberAccessException("Failed to access private method \"" + method + "\"", e);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new MemberAccessException("Private method \"" + method + "\" not exist");
|
||||
throw new MemberAccessException("Private method \"" + method + "\" not exist", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getTargetException() instanceof RuntimeException) {
|
||||
throw (RuntimeException)e.getTargetException();
|
||||
}
|
||||
throw new MemberAccessException("Invoke private method \"" + method + "\" failed with exception", e);
|
||||
}
|
||||
throw new MemberAccessException("Private method \"" + method + "\" not exist");
|
||||
throw new MemberAccessException("Private method \"" + method + "\" not found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.alibaba.testable.core.util;
|
||||
|
||||
import com.alibaba.testable.core.exception.MemberAccessException;
|
||||
import com.alibaba.testable.core.model.MockContext;
|
||||
|
||||
import java.util.HashSet;
|
||||
@@ -69,7 +70,14 @@ public class MockAssociationUtil {
|
||||
if (originMethod.equals(CONSTRUCTOR)) {
|
||||
return construct(originClass, args);
|
||||
} else if (args[0] == null) {
|
||||
return invokeStatic(originClass, originMethod, CollectionUtil.slice(args, 1));
|
||||
try {
|
||||
return invokeStatic(originClass, originMethod, CollectionUtil.slice(args, 1));
|
||||
} catch (RuntimeException e) {
|
||||
if (e instanceof MemberAccessException && e.getCause() instanceof NoSuchFieldException) {
|
||||
throw new NullPointerException("Invoking method \"" + originMethod + "\" of null object");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
return invoke(args[0], originMethod, CollectionUtil.slice(args, 1));
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.testable</groupId>
|
||||
<artifactId>testable-parent</artifactId>
|
||||
<version>0.6.9</version>
|
||||
<version>0.6.10</version>
|
||||
<relativePath>../testable-parent</relativePath>
|
||||
</parent>
|
||||
<artifactId>testable-maven-plugin</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.alibaba.testable</groupId>
|
||||
<artifactId>testable-parent</artifactId>
|
||||
<version>0.6.9</version>
|
||||
<version>0.6.10</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>testable-parent</name>
|
||||
<description>Unit test enhancement toolkit</description>
|
||||
@@ -42,7 +42,7 @@
|
||||
<plugin.gpg.version>1.6</plugin.gpg.version>
|
||||
<plugin.staging.version>1.6.8</plugin.staging.version>
|
||||
<plugin.maven.version>3.6.0</plugin.maven.version>
|
||||
<testable.version>0.6.9</testable.version>
|
||||
<testable.version>0.6.10</testable.version>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.testable</groupId>
|
||||
<artifactId>testable-parent</artifactId>
|
||||
<version>0.6.9</version>
|
||||
<version>0.6.10</version>
|
||||
<relativePath>../testable-parent</relativePath>
|
||||
</parent>
|
||||
<artifactId>testable-processor</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user