mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-02-15 22:30:24 +08:00
implementation parameter verification
This commit is contained in:
parent
4b1b1c3126
commit
d816726f4f
@ -63,13 +63,13 @@ class DemoMockServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
void should_able_to_mock_new_object() throws Exception {
|
void should_able_to_mock_new_object() throws Exception {
|
||||||
assertEquals("mock_something", demoService.newFunc());
|
assertEquals("mock_something", demoService.newFunc());
|
||||||
verify("createBlackBox").times(1);
|
verify("createBlackBox").with("something");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_able_to_mock_member_method() throws Exception {
|
void should_able_to_mock_member_method() throws Exception {
|
||||||
assertEquals("{ \"res\": \"mock_hello\"}", demoService.outerFunc("hello"));
|
assertEquals("{ \"res\": \"mock_hello\"}", demoService.outerFunc("hello"));
|
||||||
verify("innerFunc").times(1);
|
verify("innerFunc").with("hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -59,13 +59,13 @@ internal class DemoMockServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
fun should_able_to_mock_new_object() {
|
fun should_able_to_mock_new_object() {
|
||||||
assertEquals("mock_something", demoService.newFunc())
|
assertEquals("mock_something", demoService.newFunc())
|
||||||
verify("createBlackBox").times(1)
|
verify("createBlackBox").with("something")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun should_able_to_mock_member_method() {
|
fun should_able_to_mock_member_method() {
|
||||||
assertEquals("{ \"res\": \"mock_hello\"}", demoService.outerFunc("hello"))
|
assertEquals("{ \"res\": \"mock_hello\"}", demoService.outerFunc("hello"))
|
||||||
verify("innerFunc").times(1)
|
verify("innerFunc").with("hello")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -3,7 +3,6 @@ package com.alibaba.testable.demo.util
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import com.alibaba.testable.core.annotation.TestableMock
|
import com.alibaba.testable.core.annotation.TestableMock
|
||||||
import com.alibaba.testable.core.tool.TestableTool.verify
|
import com.alibaba.testable.core.tool.TestableTool.verify
|
||||||
import com.alibaba.testable.demo.util.PathUtil
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class PathUtilTest {
|
class PathUtilTest {
|
||||||
|
@ -12,5 +12,7 @@ public class ConstPool {
|
|||||||
public static final String TEST_POSTFIX = "Test";
|
public static final String TEST_POSTFIX = "Test";
|
||||||
public static final String TESTABLE_INJECT_REF = "_testableInternalRef";
|
public static final String TESTABLE_INJECT_REF = "_testableInternalRef";
|
||||||
|
|
||||||
|
public static final String FIELD_TARGET_METHOD = "targetMethod";
|
||||||
|
|
||||||
public static final String TESTABLE_MOCK = "com.alibaba.testable.core.annotation.TestableMock";
|
public static final String TESTABLE_MOCK = "com.alibaba.testable.core.annotation.TestableMock";
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.alibaba.testable.agent.handler;
|
|||||||
|
|
||||||
import com.alibaba.testable.agent.constant.ConstPool;
|
import com.alibaba.testable.agent.constant.ConstPool;
|
||||||
import com.alibaba.testable.agent.tool.ImmutablePair;
|
import com.alibaba.testable.agent.tool.ImmutablePair;
|
||||||
|
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ public class TestClassHandler extends BaseClassHandler {
|
|||||||
private static final String METHOD_CURRENT_SOURCE_METHOD_NAME = "currentSourceMethodName";
|
private static final String METHOD_CURRENT_SOURCE_METHOD_NAME = "currentSourceMethodName";
|
||||||
private static final String METHOD_RECORD_MOCK_INVOKE = "recordMockInvoke";
|
private static final String METHOD_RECORD_MOCK_INVOKE = "recordMockInvoke";
|
||||||
private static final String SIGNATURE_TESTABLE_UTIL_METHOD = "(Ljava/lang/Object;)Ljava/lang/String;";
|
private static final String SIGNATURE_TESTABLE_UTIL_METHOD = "(Ljava/lang/Object;)Ljava/lang/String;";
|
||||||
private static final String SIGNATURE_INVOKE_COUNTER_METHOD = "()V";
|
private static final String SIGNATURE_INVOKE_RECORDER_METHOD = "([Ljava/lang/Object;Z)V";
|
||||||
private static final Map<String, String> FIELD_TO_METHOD_MAPPING = new HashMap<String, String>() {{
|
private static final Map<String, String> FIELD_TO_METHOD_MAPPING = new HashMap<String, String>() {{
|
||||||
put(FIELD_TEST_CASE, METHOD_CURRENT_TEST_CASE_NAME);
|
put(FIELD_TEST_CASE, METHOD_CURRENT_TEST_CASE_NAME);
|
||||||
put(FIELD_SOURCE_METHOD, METHOD_CURRENT_SOURCE_METHOD_NAME);
|
put(FIELD_SOURCE_METHOD, METHOD_CURRENT_SOURCE_METHOD_NAME);
|
||||||
@ -104,6 +105,7 @@ public class TestClassHandler extends BaseClassHandler {
|
|||||||
il.add(getIntInsn(size));
|
il.add(getIntInsn(size));
|
||||||
il.add(new TypeInsnNode(ANEWARRAY, ClassUtil.CLASS_OBJECT));
|
il.add(new TypeInsnNode(ANEWARRAY, ClassUtil.CLASS_OBJECT));
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
|
mn.maxStack += 2;
|
||||||
il.add(new InsnNode(DUP));
|
il.add(new InsnNode(DUP));
|
||||||
il.add(getIntInsn(i));
|
il.add(getIntInsn(i));
|
||||||
ImmutablePair<Integer, Integer> code = getLoadParameterByteCode(types.get(i));
|
ImmutablePair<Integer, Integer> code = getLoadParameterByteCode(types.get(i));
|
||||||
@ -115,11 +117,26 @@ public class TestClassHandler extends BaseClassHandler {
|
|||||||
}
|
}
|
||||||
il.add(new InsnNode(AASTORE));
|
il.add(new InsnNode(AASTORE));
|
||||||
}
|
}
|
||||||
|
if (isMockForConstructor(mn)) {
|
||||||
|
il.add(new InsnNode(ICONST_1));
|
||||||
|
} else {
|
||||||
|
il.add(new InsnNode(ICONST_0));
|
||||||
|
}
|
||||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_INVOKE_RECORD_UTIL, METHOD_RECORD_MOCK_INVOKE,
|
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_INVOKE_RECORD_UTIL, METHOD_RECORD_MOCK_INVOKE,
|
||||||
SIGNATURE_INVOKE_COUNTER_METHOD, false));
|
SIGNATURE_INVOKE_RECORDER_METHOD, false));
|
||||||
mn.instructions.insertBefore(mn.instructions.get(0), il);
|
mn.instructions.insertBefore(mn.instructions.get(0), il);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isMockForConstructor(MethodNode mn) {
|
||||||
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
|
String method = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_METHOD, null);
|
||||||
|
if (ConstPool.CONSTRUCTOR.equals(method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static ImmutablePair<Integer, Integer> getLoadParameterByteCode(Byte type) {
|
private static ImmutablePair<Integer, Integer> getLoadParameterByteCode(Byte type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ClassUtil.TYPE_BYTE:
|
case ClassUtil.TYPE_BYTE:
|
||||||
|
@ -6,6 +6,7 @@ import com.alibaba.testable.agent.handler.TestClassHandler;
|
|||||||
import com.alibaba.testable.agent.tool.ImmutablePair;
|
import com.alibaba.testable.agent.tool.ImmutablePair;
|
||||||
import com.alibaba.testable.agent.model.MethodInfo;
|
import com.alibaba.testable.agent.model.MethodInfo;
|
||||||
import com.alibaba.testable.agent.tool.ComparableWeakRef;
|
import com.alibaba.testable.agent.tool.ComparableWeakRef;
|
||||||
|
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
import org.objectweb.asm.tree.AnnotationNode;
|
import org.objectweb.asm.tree.AnnotationNode;
|
||||||
@ -28,7 +29,6 @@ import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassNa
|
|||||||
public class TestableClassTransformer implements ClassFileTransformer {
|
public class TestableClassTransformer implements ClassFileTransformer {
|
||||||
|
|
||||||
private final Set<ComparableWeakRef<String>> loadedClassNames = ComparableWeakRef.getWeekHashSet();
|
private final Set<ComparableWeakRef<String>> loadedClassNames = ComparableWeakRef.getWeekHashSet();
|
||||||
private static final String TARGET_METHOD = "targetMethod";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||||
@ -90,7 +90,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
if (toDotSeparateFullClassName(an.desc).equals(ConstPool.TESTABLE_MOCK)) {
|
if (toDotSeparateFullClassName(an.desc).equals(ConstPool.TESTABLE_MOCK)) {
|
||||||
String targetClass = ClassUtil.toSlashSeparateFullClassName(methodDescPair.left);
|
String targetClass = ClassUtil.toSlashSeparateFullClassName(methodDescPair.left);
|
||||||
String targetMethod = getAnnotationParameter(an, TARGET_METHOD, mn.name);
|
String targetMethod = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_METHOD, mn.name);
|
||||||
if (targetMethod.equals(ConstPool.CONSTRUCTOR)) {
|
if (targetMethod.equals(ConstPool.CONSTRUCTOR)) {
|
||||||
String sourceClassName = ClassUtil.getSourceClassName(cn.name);
|
String sourceClassName = ClassUtil.getSourceClassName(cn.name);
|
||||||
methodInfos.add(new MethodInfo(sourceClassName, targetMethod, mn.name, mn.desc));
|
methodInfos.add(new MethodInfo(sourceClassName, targetMethod, mn.name, mn.desc));
|
||||||
@ -112,18 +112,4 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
return pos < 0 ? null : ImmutablePair.of(desc.substring(1, pos + 1), "(" + desc.substring(pos + 1));
|
return pos < 0 ? null : ImmutablePair.of(desc.substring(1, pos + 1), "(" + desc.substring(pos + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Read value of annotation parameter
|
|
||||||
*/
|
|
||||||
private <T> T getAnnotationParameter(AnnotationNode an, String key, T defaultValue) {
|
|
||||||
if (an.values != null) {
|
|
||||||
for (int i = 0; i < an.values.size(); i += 2) {
|
|
||||||
if (an.values.get(i).equals(key)) {
|
|
||||||
return (T)(an.values.get(i + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.alibaba.testable.agent.util;
|
||||||
|
|
||||||
|
import org.objectweb.asm.tree.AnnotationNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
public class AnnotationUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read value of annotation parameter
|
||||||
|
*/
|
||||||
|
public static <T> T getAnnotationParameter(AnnotationNode an, String key, T defaultValue) {
|
||||||
|
if (an.values != null) {
|
||||||
|
for (int i = 0; i < an.values.size(); i += 2) {
|
||||||
|
if (an.values.get(i).equals(key)) {
|
||||||
|
return (T)(an.values.get(i + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,23 +0,0 @@
|
|||||||
package com.alibaba.testable.agent.transformer;
|
|
||||||
|
|
||||||
import com.alibaba.testable.core.accessor.PrivateAccessor;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.objectweb.asm.tree.AnnotationNode;
|
|
||||||
|
|
||||||
import static com.alibaba.testable.agent.util.CollectionUtil.listOf;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class TestableClassTransformerTest {
|
|
||||||
|
|
||||||
private TestableClassTransformer transformer = new TestableClassTransformer();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_get_annotation_parameter() {
|
|
||||||
AnnotationNode an = new AnnotationNode("");
|
|
||||||
an.values = listOf((Object)"testKey", "testValue", "demoKey", "demoValue");
|
|
||||||
assertEquals("testValue", PrivateAccessor.invoke(transformer, "getAnnotationParameter", an, "testKey", "none"));
|
|
||||||
assertEquals("demoValue", PrivateAccessor.invoke(transformer, "getAnnotationParameter", an, "demoKey", "none"));
|
|
||||||
assertEquals("none", PrivateAccessor.invoke(transformer, "getAnnotationParameter", an, "testValue", "none"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.alibaba.testable.agent.util;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.objectweb.asm.tree.AnnotationNode;
|
||||||
|
|
||||||
|
import static com.alibaba.testable.agent.util.CollectionUtil.listOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class AnnotationUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void should_get_annotation_parameter() {
|
||||||
|
AnnotationNode an = new AnnotationNode("");
|
||||||
|
an.values = listOf((Object)"testKey", "testValue", "demoKey", "demoValue");
|
||||||
|
assertEquals("testValue", AnnotationUtil.getAnnotationParameter(an, "testKey", "none"));
|
||||||
|
assertEquals("demoValue", AnnotationUtil.getAnnotationParameter(an, "demoKey", "none"));
|
||||||
|
assertEquals("none", AnnotationUtil.getAnnotationParameter(an, "testValue", "none"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.alibaba.testable.core.accessor;
|
package com.alibaba.testable.core.accessor;
|
||||||
|
|
||||||
|
import com.alibaba.testable.core.util.InvokeRecordUtil;
|
||||||
import com.alibaba.testable.core.util.TypeUtil;
|
import com.alibaba.testable.core.util.TypeUtil;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@ -46,4 +47,18 @@ public class PrivateAccessor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> T invokeStatic(Class<?> clazz, String method, Object... args) {
|
||||||
|
try {
|
||||||
|
Class<?>[] cls = TypeUtil.getClassesFromObjects(args);
|
||||||
|
Method declaredMethod = TypeUtil.getMethodByNameAndParameterTypes(clazz.getDeclaredMethods(), method, cls);
|
||||||
|
if (declaredMethod != null) {
|
||||||
|
declaredMethod.setAccessible(true);
|
||||||
|
return (T)declaredMethod.invoke(null, args);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,24 @@ package com.alibaba.testable.core.error;
|
|||||||
*/
|
*/
|
||||||
public class VerifyFailedError extends AssertionError {
|
public class VerifyFailedError extends AssertionError {
|
||||||
|
|
||||||
public VerifyFailedError(int actualCount, int expectedCount) {
|
public VerifyFailedError(String message) {
|
||||||
super(getErrorMessage(actualCount, expectedCount));
|
super(getErrorMessage(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getErrorMessage(int actualCount, int expectedCount) {
|
public VerifyFailedError(String expected, String actual) {
|
||||||
return "\nExpected times : " + expectedCount + "\nActual times : " + actualCount;
|
super(getErrorMessage(expected, actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerifyFailedError(String message, String expected, String actual) {
|
||||||
|
super(getErrorMessage(message) + getErrorMessage(expected, actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getErrorMessage(String message) {
|
||||||
|
return "\n" + message.substring(0, 1).toUpperCase() + message.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getErrorMessage(String expected, String actual) {
|
||||||
|
return "\nExpected " + expected + "\n Actual " + actual;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package com.alibaba.testable.core.tool;
|
|
||||||
|
|
||||||
import com.alibaba.testable.core.error.VerifyFailedError;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author flin
|
|
||||||
*/
|
|
||||||
public class InvokeCounter {
|
|
||||||
|
|
||||||
private final int actualCount;
|
|
||||||
|
|
||||||
public InvokeCounter(int actualCount) {
|
|
||||||
this.actualCount = actualCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void times(int expectedCount) {
|
|
||||||
if (expectedCount != actualCount) {
|
|
||||||
throw new VerifyFailedError(actualCount, expectedCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,82 @@
|
|||||||
|
package com.alibaba.testable.core.tool;
|
||||||
|
|
||||||
|
import com.alibaba.testable.core.error.VerifyFailedError;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
public class InvokeVerifier {
|
||||||
|
|
||||||
|
private final List<Object[]> records;
|
||||||
|
|
||||||
|
public InvokeVerifier(List<Object[]> records) {
|
||||||
|
this.records = records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeVerifier with(Object arg1) {
|
||||||
|
with(new Object[]{arg1});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeVerifier with(Object arg1, Object arg2) {
|
||||||
|
with(new Object[]{arg1, arg2});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeVerifier with(Object arg1, Object arg2, Object arg3) {
|
||||||
|
with(new Object[]{arg1, arg2, arg3});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeVerifier with(Object arg1, Object arg2, Object arg3, Object arg4) {
|
||||||
|
with(new Object[]{arg1, arg2, arg3, arg4});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeVerifier with(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
|
||||||
|
with(new Object[]{arg1, arg2, arg3, arg4, arg5});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeVerifier with(Object[] args) {
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
throw new VerifyFailedError("has not more invoke");
|
||||||
|
}
|
||||||
|
Object[] record = records.get(0);
|
||||||
|
if (record.length != args.length) {
|
||||||
|
throw new VerifyFailedError(desc(args), desc(record));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (!args[i].getClass().equals(record[i].getClass())) {
|
||||||
|
throw new VerifyFailedError("parameter " + (i + 1) + " type mismatch",
|
||||||
|
": " + args[i].getClass(), ": " + record[i].getClass());
|
||||||
|
}
|
||||||
|
if (!args[i].equals(record[i])) {
|
||||||
|
throw new VerifyFailedError("parameter " + (i + 1) + " mismatched", desc(args), desc(record));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
records.remove(0);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String desc(Object[] args) {
|
||||||
|
StringBuilder sb = new StringBuilder(": ");
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append(args[i]);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeVerifier times(int expectedCount) {
|
||||||
|
if (expectedCount != records.size()) {
|
||||||
|
throw new VerifyFailedError("times: " + records.size(), "times: " + expectedCount);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,10 +27,10 @@ public class TestableTool {
|
|||||||
* Get counter to check whether specified mock method invoked
|
* Get counter to check whether specified mock method invoked
|
||||||
* @param mockMethodName name of a mock method
|
* @param mockMethodName name of a mock method
|
||||||
*/
|
*/
|
||||||
public static InvokeCounter verify(String mockMethodName) {
|
public static InvokeVerifier verify(String mockMethodName) {
|
||||||
String testClass = Thread.currentThread().getStackTrace()[InvokeRecordUtil.INDEX_OF_TEST_CLASS].getClassName();
|
String testClass = Thread.currentThread().getStackTrace()[InvokeRecordUtil.INDEX_OF_TEST_CLASS].getClassName();
|
||||||
String testCaseName = TestableUtil.currentTestCaseName(testClass);
|
String testCaseName = TestableUtil.currentTestCaseName(testClass);
|
||||||
return new InvokeCounter(InvokeRecordUtil.getInvokeCount(mockMethodName, testCaseName));
|
return new InvokeVerifier(InvokeRecordUtil.getInvokeRecord(mockMethodName, testCaseName));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.alibaba.testable.core.util;
|
package com.alibaba.testable.core.util;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -8,8 +10,11 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class InvokeRecordUtil {
|
public class InvokeRecordUtil {
|
||||||
|
|
||||||
private static final Map<String, Integer> INVOKE_RECORDS = new HashMap<String, Integer>();
|
/**
|
||||||
private final static String JOINER = "->";
|
* Mock method name -> List of invoke parameters
|
||||||
|
*/
|
||||||
|
private static final Map<String, List<Object[]>> INVOKE_RECORDS = new HashMap<String, List<Object[]>>();
|
||||||
|
private final static String JOINER = "::";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [0]Thread -> [1]TestableUtil/TestableTool -> [2]TestClass
|
* [0]Thread -> [1]TestableUtil/TestableTool -> [2]TestClass
|
||||||
@ -17,32 +22,40 @@ public class InvokeRecordUtil {
|
|||||||
public static final int INDEX_OF_TEST_CLASS = 2;
|
public static final int INDEX_OF_TEST_CLASS = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record mock method invoke count
|
* Record mock method invoke event
|
||||||
*/
|
*/
|
||||||
public static void countMockInvoke() {
|
public static void recordMockInvoke(Object[] args, boolean isConstructor) {
|
||||||
StackTraceElement mockMethodTraceElement = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS];
|
StackTraceElement mockMethodTraceElement = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS];
|
||||||
String mockMethodName = mockMethodTraceElement.getMethodName();
|
String mockMethodName = mockMethodTraceElement.getMethodName();
|
||||||
String testClass = mockMethodTraceElement.getClassName();
|
String testClass = mockMethodTraceElement.getClassName();
|
||||||
String testCaseName = TestableUtil.currentTestCaseName(testClass);
|
String testCaseName = TestableUtil.currentTestCaseName(testClass);
|
||||||
String key = testCaseName + JOINER + mockMethodName;
|
String key = testCaseName + JOINER + mockMethodName;
|
||||||
int count = getInvokeCount(mockMethodName, testCaseName);
|
List<Object[]> records = getInvokeRecord(mockMethodName, testCaseName);
|
||||||
INVOKE_RECORDS.put(key, count + 1);
|
if (isConstructor) {
|
||||||
}
|
records.add(args);
|
||||||
|
} else {
|
||||||
/**
|
records.add(slice(args, 1));
|
||||||
* Record mock method invoke event
|
}
|
||||||
*/
|
INVOKE_RECORDS.put(key, records);
|
||||||
public static void recordMockInvoke(Object[] args) {
|
|
||||||
countMockInvoke();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get mock method invoke count
|
* Get mock method invoke count
|
||||||
*/
|
*/
|
||||||
public static int getInvokeCount(String mockMethodName, String testCaseName) {
|
public static List<Object[]> getInvokeRecord(String mockMethodName, String testCaseName) {
|
||||||
String key = testCaseName + JOINER + mockMethodName;
|
String key = testCaseName + JOINER + mockMethodName;
|
||||||
Integer count = INVOKE_RECORDS.get(key);
|
List<Object[]> records = INVOKE_RECORDS.get(key);
|
||||||
return (count == null) ? 0 : count;
|
return (records == null) ? new LinkedList<Object[]>() : records;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object[] slice(Object[] args, int firstIndex) {
|
||||||
|
int size = args.length - firstIndex;
|
||||||
|
if (size <= 0) {
|
||||||
|
return new Object[0];
|
||||||
|
}
|
||||||
|
Object[] slicedArgs = new Object[size];
|
||||||
|
System.arraycopy(args, firstIndex, slicedArgs, 0, size);
|
||||||
|
return slicedArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.alibaba.testable.core.util;
|
||||||
|
|
||||||
|
import com.alibaba.testable.core.accessor.PrivateAccessor;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class InvokeRecordUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void should_slice_array() {
|
||||||
|
Object[] args = new Object[]{"1", "2", "3"};
|
||||||
|
Object[] slicedArgs = PrivateAccessor.invokeStatic(InvokeRecordUtil.class, "slice", args, 1);
|
||||||
|
assertEquals(2, slicedArgs.length);
|
||||||
|
assertEquals("2", slicedArgs[0]);
|
||||||
|
assertEquals("3", slicedArgs[1]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user