mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-10 12:20:09 +08:00
implement static method mock
This commit is contained in:
parent
989a52a048
commit
0ef13a8e95
@ -39,7 +39,7 @@ class DemoServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TestableMock
|
@TestableMock
|
||||||
private BlackBox secretBox() {
|
private BlackBox secretBox(BlackBox _) {
|
||||||
return new BlackBox("not_secret_box");
|
return new BlackBox("not_secret_box");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +91,12 @@ class DemoServiceTest {
|
|||||||
verify("startsWith").times(1);
|
verify("startsWith").times(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void should_able_to_mock_static_method() throws Exception {
|
||||||
|
assertEquals("not_secret_box", demoService.getBox().callMe());
|
||||||
|
verify("secretBox").times(1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_able_to_get_source_method_name() throws Exception {
|
void should_able_to_get_source_method_name() throws Exception {
|
||||||
// synchronous
|
// synchronous
|
||||||
|
@ -28,7 +28,7 @@ class BlackBox(private val data: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object ColorBox {
|
object ColorBox {
|
||||||
fun createBox(color: String): BlackBox {
|
fun createBox(color: String, box: BlackBox): BlackBox {
|
||||||
return BlackBox("${color}_Box")
|
return BlackBox("${color}_${box.callMe()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,13 @@ class DemoService {
|
|||||||
return box.trim() + "__" + box.substring(1, 2) + "__" + box.startsWith("any")
|
return box.trim() + "__" + box.substring(1, 2) + "__" + box.startsWith("any")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target 6 - method with static method invoke
|
||||||
|
*/
|
||||||
|
fun getBox(): BlackBox {
|
||||||
|
return ColorBox.createBox("Red", BlackBox.secretBox())
|
||||||
|
}
|
||||||
|
|
||||||
fun callerOne(): String {
|
fun callerOne(): String {
|
||||||
return callFromDifferentMethod()
|
return callFromDifferentMethod()
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.alibaba.testable.demo
|
package com.alibaba.testable.demo
|
||||||
|
|
||||||
import com.alibaba.testable.core.accessor.PrivateAccessor
|
import com.alibaba.testable.core.accessor.PrivateAccessor
|
||||||
import com.alibaba.testable.processor.annotation.EnablePrivateAccess
|
|
||||||
import com.alibaba.testable.core.annotation.TestableMock
|
import com.alibaba.testable.core.annotation.TestableMock
|
||||||
import com.alibaba.testable.core.tool.TestableTool.*
|
import com.alibaba.testable.core.tool.TestableTool.*
|
||||||
|
import com.alibaba.testable.processor.annotation.EnablePrivateAccess
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@ -27,6 +27,16 @@ internal class DemoServiceTest {
|
|||||||
@TestableMock
|
@TestableMock
|
||||||
private fun startsWith(self: BlackBox, s: String) = false
|
private fun startsWith(self: BlackBox, s: String) = false
|
||||||
|
|
||||||
|
@TestableMock
|
||||||
|
private fun secretBox(ignore: BlackBox): BlackBox {
|
||||||
|
return BlackBox("not_secret_box")
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestableMock
|
||||||
|
private fun createBox(ignore: ColorBox, color: String, box: BlackBox): BlackBox {
|
||||||
|
return BlackBox("White_${box.callMe()}")
|
||||||
|
}
|
||||||
|
|
||||||
@TestableMock
|
@TestableMock
|
||||||
private fun callFromDifferentMethod(self: DemoService): String {
|
private fun callFromDifferentMethod(self: DemoService): String {
|
||||||
return if (TEST_CASE == "should_able_to_get_test_case_name") {
|
return if (TEST_CASE == "should_able_to_get_test_case_name") {
|
||||||
@ -73,6 +83,13 @@ internal class DemoServiceTest {
|
|||||||
verify("startsWith").times(1)
|
verify("startsWith").times(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun should_able_to_mock_static_method() {
|
||||||
|
assertEquals("White_not_secret_box", demoService.getBox().callMe())
|
||||||
|
verify("secretBox").times(1)
|
||||||
|
verify("createBox").times(1)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun should_able_to_get_source_method_name() {
|
fun should_able_to_get_source_method_name() {
|
||||||
// synchronous
|
// synchronous
|
||||||
|
@ -19,6 +19,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
|
|
||||||
private final List<MethodInfo> injectMethods;
|
private final List<MethodInfo> injectMethods;
|
||||||
private final Set<Integer> invokeOps = new HashSet<Integer>() {{
|
private final Set<Integer> invokeOps = new HashSet<Integer>() {{
|
||||||
|
add(Opcodes.INVOKESTATIC);
|
||||||
add(Opcodes.INVOKESPECIAL);
|
add(Opcodes.INVOKESPECIAL);
|
||||||
add(Opcodes.INVOKEVIRTUAL);
|
add(Opcodes.INVOKEVIRTUAL);
|
||||||
}};
|
}};
|
||||||
@ -60,7 +61,8 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
// it's a member method and an inject method for it exist
|
// it's a member method and an inject method for it exist
|
||||||
int rangeStart = getMemberMethodStart(instructions, i);
|
int rangeStart = getMemberMethodStart(instructions, i);
|
||||||
if (rangeStart >= 0) {
|
if (rangeStart >= 0) {
|
||||||
instructions = replaceMemberCallOps(cn, mn, instructions, node.owner, memberInjectMethodName, rangeStart, i);
|
instructions = replaceMemberCallOps(cn, mn, memberInjectMethodName, instructions,
|
||||||
|
node.owner, node.getOpcode(), rangeStart, i);
|
||||||
i = rangeStart;
|
i = rangeStart;
|
||||||
}
|
}
|
||||||
} else if (ConstPool.CONSTRUCTOR.equals(node.name)) {
|
} else if (ConstPool.CONSTRUCTOR.equals(node.name)) {
|
||||||
@ -123,6 +125,9 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
case Opcodes.INVOKESTATIC:
|
case Opcodes.INVOKESTATIC:
|
||||||
stackLevel += ClassUtil.getParameterTypes(((MethodInsnNode)instructions[i]).desc).size();
|
stackLevel += ClassUtil.getParameterTypes(((MethodInsnNode)instructions[i]).desc).size();
|
||||||
break;
|
break;
|
||||||
|
case -1:
|
||||||
|
// reach LineNumberNode or LabelNode
|
||||||
|
return i + 1;
|
||||||
default:
|
default:
|
||||||
stackLevel -= BytecodeUtil.stackEffect(instructions[i].getOpcode());
|
stackLevel -= BytecodeUtil.stackEffect(instructions[i].getOpcode());
|
||||||
}
|
}
|
||||||
@ -153,13 +158,18 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
ClassUtil.toByteCodeClassName(classType);
|
ClassUtil.toByteCodeClassName(classType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractInsnNode[] replaceMemberCallOps(ClassNode cn, MethodNode mn, AbstractInsnNode[] instructions,
|
private AbstractInsnNode[] replaceMemberCallOps(ClassNode cn, MethodNode mn, String substitutionMethod,
|
||||||
String ownerClass, String substitutionMethod, int start, int end) {
|
AbstractInsnNode[] instructions, String ownerClass,
|
||||||
|
int opcode, int start, int end) {
|
||||||
mn.maxStack++;
|
mn.maxStack++;
|
||||||
MethodInsnNode method = (MethodInsnNode)instructions[end];
|
MethodInsnNode method = (MethodInsnNode)instructions[end];
|
||||||
String testClassName = ClassUtil.getTestClassName(cn.name);
|
String testClassName = ClassUtil.getTestClassName(cn.name);
|
||||||
mn.instructions.insertBefore(instructions[start], new FieldInsnNode(GETSTATIC, testClassName,
|
mn.instructions.insertBefore(instructions[start], new FieldInsnNode(GETSTATIC, testClassName,
|
||||||
ConstPool.TESTABLE_INJECT_REF, ClassUtil.toByteCodeClassName(testClassName)));
|
ConstPool.TESTABLE_INJECT_REF, ClassUtil.toByteCodeClassName(testClassName)));
|
||||||
|
if (Opcodes.INVOKESTATIC == opcode) {
|
||||||
|
// append a null value if it was a static invoke
|
||||||
|
mn.instructions.insertBefore(instructions[start], new InsnNode(ACONST_NULL));
|
||||||
|
}
|
||||||
mn.instructions.insertBefore(instructions[end], new MethodInsnNode(INVOKEVIRTUAL, testClassName,
|
mn.instructions.insertBefore(instructions[end], new MethodInsnNode(INVOKEVIRTUAL, testClassName,
|
||||||
substitutionMethod, addFirstParameter(method.desc, ownerClass), false));
|
substitutionMethod, addFirstParameter(method.desc, ownerClass), false));
|
||||||
mn.instructions.remove(instructions[end]);
|
mn.instructions.remove(instructions[end]);
|
||||||
|
Loading…
Reference in New Issue
Block a user