mirror of
https://github.com/alibaba/testable-mock.git
synced 2026-08-22 19:23:29 +08:00
use TEST_CASE and SOURCE_METHOD instead of method call
This commit is contained in:
@@ -5,13 +5,26 @@ import com.alibaba.testable.agent.util.ClassUtil;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class TestClassHandler extends BaseClassHandler {
|
||||
|
||||
private static final String CLASS_TESTABLE_UTIL = "com/alibaba/testable/core/util/TestableUtil";
|
||||
private static final String FIELD_TEST_CASE = "TEST_CASE";
|
||||
private static final String FIELD_SOURCE_METHOD = "SOURCE_METHOD";
|
||||
private static final String METHOD_CURRENT_TEST_CASE_NAME = "currentTestCaseName";
|
||||
private static final String METHOD_CURRENT_SOURCE_METHOD_NAME = "currentSourceMethodName";
|
||||
private static final String SIGNATURE_TESTABLE_UTIL_METHOD = "(Ljava/lang/Object;)Ljava/lang/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_SOURCE_METHOD, METHOD_CURRENT_SOURCE_METHOD_NAME);
|
||||
}};
|
||||
|
||||
/**
|
||||
* Handle bytecode of test class
|
||||
* @param cn original class node
|
||||
@@ -26,6 +39,11 @@ public class TestClassHandler extends BaseClassHandler {
|
||||
}
|
||||
|
||||
private void transformMethod(ClassNode cn, MethodNode mn) {
|
||||
handleAnnotation(cn, mn);
|
||||
handleInstruction(cn, mn);
|
||||
}
|
||||
|
||||
private void handleAnnotation(ClassNode cn, MethodNode mn) {
|
||||
List<String> visibleAnnotationNames = new ArrayList<String>();
|
||||
if (mn.visibleAnnotations == null) {
|
||||
return;
|
||||
@@ -42,6 +60,36 @@ public class TestClassHandler extends BaseClassHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleInstruction(ClassNode cn, MethodNode mn) {
|
||||
AbstractInsnNode[] instructions = mn.instructions.toArray();
|
||||
int i = 0;
|
||||
do {
|
||||
if (instructions[i].getOpcode() == GETSTATIC) {
|
||||
FieldInsnNode fieldInsnNode = (FieldInsnNode)instructions[i];
|
||||
if (isTestableUtilField(fieldInsnNode)) {
|
||||
instructions = replaceTestableUtilField(mn, instructions, fieldInsnNode.name, i);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
} while (i < instructions.length);
|
||||
}
|
||||
|
||||
private boolean isTestableUtilField(FieldInsnNode fieldInsnNode) {
|
||||
return fieldInsnNode.owner.equals(CLASS_TESTABLE_UTIL) &&
|
||||
(fieldInsnNode.name.equals(FIELD_TEST_CASE) || fieldInsnNode.name.equals(FIELD_SOURCE_METHOD));
|
||||
}
|
||||
|
||||
private AbstractInsnNode[] replaceTestableUtilField(MethodNode mn, AbstractInsnNode[] instructions,
|
||||
String fieldName, int pos) {
|
||||
InsnList insnNodes = new InsnList();
|
||||
insnNodes.insert(new VarInsnNode(ALOAD, 0));
|
||||
insnNodes.insert(new MethodInsnNode(INVOKESTATIC, CLASS_TESTABLE_UTIL, FIELD_TO_METHOD_MAPPING.get(fieldName),
|
||||
SIGNATURE_TESTABLE_UTIL_METHOD, false));
|
||||
mn.instructions.insertBefore(instructions[pos], insnNodes);
|
||||
mn.instructions.remove(instructions[pos]);
|
||||
return mn.instructions.toArray();
|
||||
}
|
||||
|
||||
private void injectTestableRef(ClassNode cn, MethodNode mn) {
|
||||
InsnList il = new InsnList();
|
||||
il.add(new VarInsnNode(ALOAD, 0));
|
||||
|
||||
@@ -7,6 +7,9 @@ import com.alibaba.testable.core.constant.ConstPool;
|
||||
*/
|
||||
public class TestableUtil {
|
||||
|
||||
public static String TEST_CASE;
|
||||
public static String SOURCE_METHOD;
|
||||
|
||||
public static String currentSourceMethodName(Object testClassRef) {
|
||||
Class<?> testClass = testClassRef.getClass();
|
||||
StackTraceElement[] stack = getMainThread().getStackTrace();
|
||||
|
||||
@@ -3,11 +3,12 @@ package com.alibaba.testable.demo;
|
||||
import com.alibaba.testable.core.accessor.PrivateAccessor;
|
||||
import com.alibaba.testable.core.annotation.EnableTestable;
|
||||
import com.alibaba.testable.core.annotation.TestableInject;
|
||||
import com.alibaba.testable.core.util.TestableUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import static com.alibaba.testable.core.util.TestableUtil.SOURCE_METHOD;
|
||||
import static com.alibaba.testable.core.util.TestableUtil.TEST_CASE;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@EnableTestable
|
||||
@@ -40,7 +41,7 @@ class DemoServiceTest {
|
||||
|
||||
@TestableInject
|
||||
private String callFromDifferentMethod() {
|
||||
switch (TestableUtil.currentSourceMethodName(this)) {
|
||||
switch (SOURCE_METHOD) {
|
||||
case "callerOne": return "mock_one";
|
||||
default: return "mock_others";
|
||||
}
|
||||
@@ -80,25 +81,15 @@ class DemoServiceTest {
|
||||
|
||||
@Test
|
||||
void should_able_to_get_source_method_name() throws Exception {
|
||||
assertEquals("mock_one", demoService.callerOne());
|
||||
assertEquals("mock_others", demoService.callerTwo());
|
||||
assertEquals("mock_one_mock_others", new Callable<String>() {
|
||||
@Override
|
||||
public String call() {
|
||||
return demoService.callerOne() + "_" + demoService.callerTwo();
|
||||
}
|
||||
}.call());
|
||||
assertEquals("mock_one_mock_others", demoService.callerTwo() + "_" + demoService.callerOne());
|
||||
assertEquals("mock_one_mock_others", ((Callable<String>)() ->
|
||||
demoService.callerOne() + "_" + demoService.callerTwo()).call());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_able_to_get_test_case_name() throws Exception {
|
||||
assertEquals("should_able_to_get_test_case_name", TestableUtil.currentTestCaseName(this));
|
||||
assertEquals("should_able_to_get_test_case_name", new Callable<String>() {
|
||||
@Override
|
||||
public String call() {
|
||||
return TestableUtil.currentTestCaseName(this);
|
||||
}
|
||||
}.call());
|
||||
assertEquals("should_able_to_get_test_case_name", TEST_CASE);
|
||||
assertEquals("should_able_to_get_test_case_name", ((Callable<String>)() -> TEST_CASE).call());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@ package com.alibaba.testable.demo
|
||||
import com.alibaba.testable.core.accessor.PrivateAccessor
|
||||
import com.alibaba.testable.core.annotation.EnableTestable
|
||||
import com.alibaba.testable.core.annotation.TestableInject
|
||||
import com.alibaba.testable.core.util.TestableUtil
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import com.alibaba.testable.core.util.TestableUtil.SOURCE_METHOD
|
||||
import com.alibaba.testable.core.util.TestableUtil.TEST_CASE
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
@@ -28,7 +29,7 @@ internal class DemoServiceTest {
|
||||
private fun startsWith(self: BlackBox, s: String) = false
|
||||
|
||||
@TestableInject
|
||||
private fun callFromDifferentMethod() = when (TestableUtil.currentSourceMethodName(this)) {
|
||||
private fun callFromDifferentMethod() = when (SOURCE_METHOD) {
|
||||
"callerOne" -> "mock_one"
|
||||
else -> "mock_others"
|
||||
}
|
||||
@@ -37,45 +38,42 @@ internal class DemoServiceTest {
|
||||
|
||||
@Test
|
||||
fun should_able_to_test_private_method() {
|
||||
Assertions.assertEquals("hello - 1", PrivateAccessor.invoke(demoService, "privateFunc", "hello", 1))
|
||||
assertEquals("hello - 1", PrivateAccessor.invoke(demoService, "privateFunc", "hello", 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_able_to_test_private_field() {
|
||||
PrivateAccessor.set(demoService, "count", 3)
|
||||
Assertions.assertEquals("5", demoService.privateFieldAccessFunc())
|
||||
Assertions.assertEquals(5, PrivateAccessor.get(demoService, "count"))
|
||||
assertEquals("5", demoService.privateFieldAccessFunc())
|
||||
assertEquals(5, PrivateAccessor.get(demoService, "count"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_able_to_test_new_object() {
|
||||
Assertions.assertEquals("mock_something", demoService.newFunc())
|
||||
assertEquals("mock_something", demoService.newFunc())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_able_to_test_member_method() {
|
||||
Assertions.assertEquals("{ \"res\": \"mock_hello\"}", demoService.outerFunc("hello"))
|
||||
assertEquals("{ \"res\": \"mock_hello\"}", demoService.outerFunc("hello"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_able_to_test_common_method() {
|
||||
Assertions.assertEquals("trim_string__sub_string__false", demoService.commonFunc())
|
||||
assertEquals("trim_string__sub_string__false", demoService.commonFunc())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_able_to_get_source_method_name() {
|
||||
Assertions.assertEquals("mock_one", demoService.callerOne())
|
||||
Assertions.assertEquals("mock_others", demoService.callerTwo())
|
||||
Assertions.assertEquals("mock_one_mock_others", Callable<String> {
|
||||
assertEquals("mock_one_mock_others", demoService.callerTwo() + "_" + demoService.callerOne())
|
||||
assertEquals("mock_one_mock_others", Callable<String> {
|
||||
demoService.callerOne() + "_" + demoService.callerTwo()
|
||||
}.call())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun should_able_to_get_test_case_name() {
|
||||
Assertions.assertEquals("should_able_to_get_test_case_name", TestableUtil.currentTestCaseName(this))
|
||||
Assertions.assertEquals("should_able_to_get_test_case_name", Callable<String> {
|
||||
TestableUtil.currentTestCaseName(this)
|
||||
}.call())
|
||||
assertEquals("should_able_to_get_test_case_name", TEST_CASE)
|
||||
assertEquals("should_able_to_get_test_case_name", Callable<String> { TEST_CASE }.call())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user