mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-26 12:20:39 +08:00
add mock verifier support spock
This commit is contained in:
parent
46706ba56c
commit
58da25de53
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<font size="5">**0.6版本已发布**</font>,从`0.5.x`升级到`0.6.x`版本请参考[版本升级指南](https://alibaba.github.io/testable-mock/#/zh-cn/doc/upgrade-guide)
|
<font size="5">**0.6版本已发布**</font>,从`0.5.x`升级到`0.6.x`版本请参考[版本升级指南](https://alibaba.github.io/testable-mock/#/zh-cn/doc/upgrade-guide)
|
||||||
|
|
||||||
如果有遇到其他任何使用问题和建议,请直接在[Issue](https://github.com/alibaba/testable-mock/issues)中提出,也可通过[Pull Request](https://github.com/alibaba/testable-mock/pulls)提交您的代码,我们将在24小时内回复并处理
|
如果有遇到其他任何使用问题和建议,请直接在[Issues](https://github.com/alibaba/testable-mock/issues)中提出,也可通过[Pull Request](https://github.com/alibaba/testable-mock/pulls)提交您的代码,我们将在24小时内回复并处理
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -2,17 +2,12 @@ package com.alibaba.testable.agent.handler;
|
|||||||
|
|
||||||
import com.alibaba.testable.agent.handler.test.*;
|
import com.alibaba.testable.agent.handler.test.*;
|
||||||
import com.alibaba.testable.agent.model.TestCaseMethodType;
|
import com.alibaba.testable.agent.model.TestCaseMethodType;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
|
||||||
import com.alibaba.testable.core.util.LogUtil;
|
import com.alibaba.testable.core.util.LogUtil;
|
||||||
import org.objectweb.asm.Label;
|
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static com.alibaba.testable.core.constant.ConstPool.THIS_REF;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author flin
|
* @author flin
|
||||||
*/
|
*/
|
||||||
@ -24,12 +19,14 @@ public class TestClassHandler extends BaseClassWithContextHandler {
|
|||||||
private static final String DESC_METHOD_CLEAN = "()V";
|
private static final String DESC_METHOD_CLEAN = "()V";
|
||||||
|
|
||||||
private int testCaseCount = 0;
|
private int testCaseCount = 0;
|
||||||
|
private boolean shouldGenerateCleanupMethod = true;
|
||||||
|
|
||||||
private final Framework[] frameworkClasses = new Framework[] {
|
private final Framework[] frameworkClasses = new Framework[] {
|
||||||
new JUnit4Framework(),
|
new JUnit4Framework(),
|
||||||
new JUnit5Framework(),
|
new JUnit5Framework(),
|
||||||
new TestNgFramework(),
|
new TestNgFramework(),
|
||||||
new TestNgOnClassFramework()
|
new TestNgOnClassFramework(),
|
||||||
|
new SpockFramework()
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,13 +40,15 @@ public class TestClassHandler extends BaseClassWithContextHandler {
|
|||||||
LogUtil.warn("Failed to detect test framework for %s", cn.name);
|
LogUtil.warn("Failed to detect test framework for %s", cn.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!framework.hasTestAfterMethod) {
|
|
||||||
addTestAfterMethod(cn, framework.getTestAfterAnnotation());
|
|
||||||
}
|
|
||||||
for (MethodNode mn : cn.methods) {
|
for (MethodNode mn : cn.methods) {
|
||||||
handleTestableUtil(mn);
|
handleTestableUtil(mn);
|
||||||
handleTestCaseMethod(mn, framework);
|
handleTestCaseMethod(mn, framework);
|
||||||
}
|
}
|
||||||
|
if (shouldGenerateCleanupMethod) {
|
||||||
|
MethodNode cleanupMethod = framework.getCleanupMethod(cn.name);
|
||||||
|
injectMockContextClean(cleanupMethod);
|
||||||
|
cn.methods.add(cleanupMethod);
|
||||||
|
}
|
||||||
LogUtil.diagnose(" Found %d test cases", testCaseCount);
|
LogUtil.diagnose(" Found %d test cases", testCaseCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,23 +75,6 @@ public class TestClassHandler extends BaseClassWithContextHandler {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTestAfterMethod(ClassNode cn, String testAfterAnnotation) {
|
|
||||||
MethodNode afterTestMethod = new MethodNode(ACC_PUBLIC, "testableAfterTestCase", "()V", null, null);
|
|
||||||
afterTestMethod.visibleAnnotations = Collections.singletonList(new AnnotationNode(testAfterAnnotation));
|
|
||||||
InsnList il = new InsnList();
|
|
||||||
LabelNode startLabel = new LabelNode(new Label());
|
|
||||||
LabelNode endLabel = new LabelNode(new Label());
|
|
||||||
il.add(startLabel);
|
|
||||||
il.add(new InsnNode(RETURN));
|
|
||||||
il.add(endLabel);
|
|
||||||
afterTestMethod.instructions = il;
|
|
||||||
afterTestMethod.localVariables = Collections.singletonList(new LocalVariableNode(THIS_REF,
|
|
||||||
ClassUtil.toByteCodeClassName(cn.name), null, startLabel, endLabel, 0));
|
|
||||||
afterTestMethod.maxLocals = 1;
|
|
||||||
afterTestMethod.maxStack = 0;
|
|
||||||
cn.methods.add(afterTestMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleTestCaseMethod(MethodNode mn, Framework framework) {
|
private void handleTestCaseMethod(MethodNode mn, Framework framework) {
|
||||||
TestCaseMethodType type = framework.checkMethodType(mn);
|
TestCaseMethodType type = framework.checkMethodType(mn);
|
||||||
if (type.equals(TestCaseMethodType.TEST)) {
|
if (type.equals(TestCaseMethodType.TEST)) {
|
||||||
@ -101,6 +83,7 @@ public class TestClassHandler extends BaseClassWithContextHandler {
|
|||||||
testCaseCount++;
|
testCaseCount++;
|
||||||
} else if (type.equals(TestCaseMethodType.AFTER_TEST)) {
|
} else if (type.equals(TestCaseMethodType.AFTER_TEST)) {
|
||||||
injectMockContextClean(mn);
|
injectMockContextClean(mn);
|
||||||
|
shouldGenerateCleanupMethod = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
package com.alibaba.testable.agent.handler.test;
|
||||||
|
|
||||||
|
import com.alibaba.testable.agent.model.TestCaseMethodType;
|
||||||
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
|
import com.alibaba.testable.agent.util.CollectionUtil;
|
||||||
|
import org.objectweb.asm.Label;
|
||||||
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.alibaba.testable.core.constant.ConstPool.THIS_REF;
|
||||||
|
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||||
|
import static org.objectweb.asm.Opcodes.RETURN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
abstract public class CommonFramework implements Framework {
|
||||||
|
|
||||||
|
private static final String DEFAULT_CLEANUP_METHOD = "testableCleanup";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean fit(Set<String> classAnnotations, Set<String> methodAnnotations) {
|
||||||
|
return CollectionUtil.containsAny(methodAnnotations, getTestMethodAnnotations());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestCaseMethodType checkMethodType(MethodNode mn) {
|
||||||
|
if (mn.visibleAnnotations == null) {
|
||||||
|
return TestCaseMethodType.OTHERS;
|
||||||
|
}
|
||||||
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
|
if (getTestMethodAnnotations().contains(an.desc)) {
|
||||||
|
return TestCaseMethodType.TEST;
|
||||||
|
} else if (an.desc.equals(getCleanupMethodAnnotation())) {
|
||||||
|
return TestCaseMethodType.AFTER_TEST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TestCaseMethodType.OTHERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodNode getCleanupMethod(String className) {
|
||||||
|
MethodNode cleanupMethod = new MethodNode(ACC_PUBLIC, DEFAULT_CLEANUP_METHOD, "()V", null, null);
|
||||||
|
cleanupMethod.visibleAnnotations = Collections.singletonList(new AnnotationNode(getCleanupMethodAnnotation()));
|
||||||
|
InsnList il = new InsnList();
|
||||||
|
LabelNode startLabel = new LabelNode(new Label());
|
||||||
|
LabelNode endLabel = new LabelNode(new Label());
|
||||||
|
il.add(startLabel);
|
||||||
|
il.add(new InsnNode(RETURN));
|
||||||
|
il.add(endLabel);
|
||||||
|
cleanupMethod.instructions = il;
|
||||||
|
cleanupMethod.localVariables = Collections.singletonList(new LocalVariableNode(THIS_REF,
|
||||||
|
ClassUtil.toByteCodeClassName(className), null, startLabel, endLabel, 0));
|
||||||
|
cleanupMethod.maxLocals = 1;
|
||||||
|
cleanupMethod.maxStack = 0;
|
||||||
|
return cleanupMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all annotations that identify test case method
|
||||||
|
* @return list of annotation full name
|
||||||
|
*/
|
||||||
|
public abstract List<String> getTestMethodAnnotations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get annotation that identify test cleanup method
|
||||||
|
* @return full name of cleanup method annotation
|
||||||
|
*/
|
||||||
|
public abstract String getCleanupMethodAnnotation();
|
||||||
|
|
||||||
|
}
|
@ -1,16 +1,14 @@
|
|||||||
package com.alibaba.testable.agent.handler.test;
|
package com.alibaba.testable.agent.handler.test;
|
||||||
|
|
||||||
import com.alibaba.testable.agent.model.TestCaseMethodType;
|
import com.alibaba.testable.agent.model.TestCaseMethodType;
|
||||||
import com.alibaba.testable.agent.util.CollectionUtil;
|
|
||||||
import org.objectweb.asm.tree.AnnotationNode;
|
|
||||||
import org.objectweb.asm.tree.MethodNode;
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
abstract public class Framework {
|
/**
|
||||||
|
* @author flin
|
||||||
public boolean hasTestAfterMethod;
|
*/
|
||||||
|
public interface Framework {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the test class using current test framework
|
* Check whether the test class using current test framework
|
||||||
@ -18,31 +16,20 @@ abstract public class Framework {
|
|||||||
* @param methodAnnotations annotations of all methods
|
* @param methodAnnotations annotations of all methods
|
||||||
* @return fit or not
|
* @return fit or not
|
||||||
*/
|
*/
|
||||||
public boolean fit(Set<String> classAnnotations, Set<String> methodAnnotations) {
|
boolean fit(Set<String> classAnnotations, Set<String> methodAnnotations);
|
||||||
if (methodAnnotations.contains(getTestAfterAnnotation())) {
|
|
||||||
hasTestAfterMethod = true;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return CollectionUtil.containsAny(methodAnnotations, getTestAnnotations());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestCaseMethodType checkMethodType(MethodNode mn) {
|
/**
|
||||||
if (mn.visibleAnnotations == null) {
|
* Check whether a method is test or cleanup method
|
||||||
return TestCaseMethodType.OTHERS;
|
* @param mn method node
|
||||||
}
|
* @return test method / cleanup method / other method
|
||||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
*/
|
||||||
if (getTestAnnotations().contains(an.desc)) {
|
TestCaseMethodType checkMethodType(MethodNode mn);
|
||||||
return TestCaseMethodType.TEST;
|
|
||||||
} else if (an.desc.equals(getTestAfterAnnotation())) {
|
|
||||||
return TestCaseMethodType.AFTER_TEST;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TestCaseMethodType.OTHERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract List<String> getTestAnnotations();
|
/**
|
||||||
|
* Generate cleanup method with correct name and annotations
|
||||||
public abstract String getTestAfterAnnotation();
|
* @param className full name of test class
|
||||||
|
* @return cleanup method for current framework
|
||||||
|
*/
|
||||||
|
MethodNode getCleanupMethod(String className);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,18 @@ package com.alibaba.testable.agent.handler.test;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class JUnit4Framework extends Framework {
|
public class JUnit4Framework extends CommonFramework {
|
||||||
|
|
||||||
public static final String ANNOTATION_TEST = "Lorg/junit/Test;";
|
public static final String ANNOTATION_TEST = "Lorg/junit/Test;";
|
||||||
private static final String ANNOTATION_AFTER_TEST = "Lorg/junit/After;";
|
private static final String ANNOTATION_CLEANUP = "Lorg/junit/After;";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getTestAnnotations() {
|
public List<String> getTestMethodAnnotations() {
|
||||||
return Collections.singletonList(ANNOTATION_TEST);
|
return Collections.singletonList(ANNOTATION_TEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTestAfterAnnotation() {
|
public String getCleanupMethodAnnotation() {
|
||||||
return ANNOTATION_AFTER_TEST;
|
return ANNOTATION_CLEANUP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,19 +3,19 @@ package com.alibaba.testable.agent.handler.test;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class JUnit5Framework extends Framework {
|
public class JUnit5Framework extends CommonFramework {
|
||||||
|
|
||||||
public static final String ANNOTATION_TEST = "Lorg/junit/jupiter/api/Test;";
|
public static final String ANNOTATION_TEST = "Lorg/junit/jupiter/api/Test;";
|
||||||
public static final String ANNOTATION_PARAMETERIZED_TEST = "Lorg/junit/jupiter/params/ParameterizedTest;";
|
public static final String ANNOTATION_PARAMETERIZED_TEST = "Lorg/junit/jupiter/params/ParameterizedTest;";
|
||||||
private static final String ANNOTATION_AFTER_TEST = "Lorg/junit/jupiter/api/AfterEach;";
|
private static final String ANNOTATION_CLEANUP = "Lorg/junit/jupiter/api/AfterEach;";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getTestAnnotations() {
|
public List<String> getTestMethodAnnotations() {
|
||||||
return Arrays.asList(ANNOTATION_TEST, ANNOTATION_PARAMETERIZED_TEST);
|
return Arrays.asList(ANNOTATION_TEST, ANNOTATION_PARAMETERIZED_TEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTestAfterAnnotation() {
|
public String getCleanupMethodAnnotation() {
|
||||||
return ANNOTATION_AFTER_TEST;
|
return ANNOTATION_CLEANUP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.alibaba.testable.agent.handler.test;
|
||||||
|
|
||||||
|
import com.alibaba.testable.agent.model.TestCaseMethodType;
|
||||||
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
|
import org.objectweb.asm.Label;
|
||||||
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.alibaba.testable.core.constant.ConstPool.THIS_REF;
|
||||||
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
|
public class SpockFramework implements Framework {
|
||||||
|
|
||||||
|
public static final String ANNOTATION_TEST = "Lorg/spockframework/runtime/model/FeatureMetadata;";
|
||||||
|
private static final String NAME_CLEANUP = "cleanup";
|
||||||
|
private static final String DESC_CLEANUP = "()Ljava/lang/Object;";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean fit(Set<String> classAnnotations, Set<String> methodAnnotations) {
|
||||||
|
return methodAnnotations.contains(ANNOTATION_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestCaseMethodType checkMethodType(MethodNode mn) {
|
||||||
|
if (NAME_CLEANUP.equals(mn.name)) {
|
||||||
|
return TestCaseMethodType.AFTER_TEST;
|
||||||
|
} else if (mn.visibleAnnotations == null) {
|
||||||
|
return TestCaseMethodType.OTHERS;
|
||||||
|
}
|
||||||
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
|
if (ANNOTATION_TEST.equals(an.desc)) {
|
||||||
|
return TestCaseMethodType.TEST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TestCaseMethodType.OTHERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodNode getCleanupMethod(String className) {
|
||||||
|
MethodNode cleanupMethod = new MethodNode(ACC_PRIVATE, NAME_CLEANUP, DESC_CLEANUP, null, null);
|
||||||
|
InsnList il = new InsnList();
|
||||||
|
LabelNode startLabel = new LabelNode(new Label());
|
||||||
|
LabelNode endLabel = new LabelNode(new Label());
|
||||||
|
il.add(startLabel);
|
||||||
|
il.add(new InsnNode(ACONST_NULL));
|
||||||
|
il.add(new InsnNode(ARETURN));
|
||||||
|
il.add(endLabel);
|
||||||
|
cleanupMethod.instructions = il;
|
||||||
|
cleanupMethod.localVariables = Collections.singletonList(new LocalVariableNode(THIS_REF,
|
||||||
|
ClassUtil.toByteCodeClassName(className), null, startLabel, endLabel, 0));
|
||||||
|
cleanupMethod.maxLocals = 1;
|
||||||
|
cleanupMethod.maxStack = 1;
|
||||||
|
return cleanupMethod;
|
||||||
|
}
|
||||||
|
}
|
@ -3,18 +3,18 @@ package com.alibaba.testable.agent.handler.test;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TestNgFramework extends Framework {
|
public class TestNgFramework extends CommonFramework {
|
||||||
|
|
||||||
private static final String ANNOTATION_TEST = "Lorg/testng/annotations/Test;";
|
private static final String ANNOTATION_TEST = "Lorg/testng/annotations/Test;";
|
||||||
private static final String ANNOTATION_AFTER_TEST = "Lorg/testng/annotations/AfterMethod;";
|
private static final String ANNOTATION_CLEANUP = "Lorg/testng/annotations/AfterMethod;";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getTestAnnotations() {
|
public List<String> getTestMethodAnnotations() {
|
||||||
return Collections.singletonList(ANNOTATION_TEST);
|
return Collections.singletonList(ANNOTATION_TEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTestAfterAnnotation() {
|
public String getCleanupMethodAnnotation() {
|
||||||
return ANNOTATION_AFTER_TEST;
|
return ANNOTATION_CLEANUP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,7 @@ public class TestNgOnClassFramework extends TestNgFramework {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean fit(Set<String> classAnnotations, Set<String> methodAnnotations) {
|
public boolean fit(Set<String> classAnnotations, Set<String> methodAnnotations) {
|
||||||
if (CollectionUtil.containsAny(classAnnotations, getTestAnnotations())) {
|
return CollectionUtil.containsAny(classAnnotations, getTestMethodAnnotations());
|
||||||
if (methodAnnotations.contains(getTestAfterAnnotation())) {
|
|
||||||
hasTestAfterMethod = true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -28,7 +22,7 @@ public class TestNgOnClassFramework extends TestNgFramework {
|
|||||||
return (mn.access & ACC_PUBLIC) != 0 ? TestCaseMethodType.TEST : TestCaseMethodType.OTHERS;
|
return (mn.access & ACC_PUBLIC) != 0 ? TestCaseMethodType.TEST : TestCaseMethodType.OTHERS;
|
||||||
}
|
}
|
||||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
if (an.desc.equals(getTestAfterAnnotation())) {
|
if (an.desc.equals(getCleanupMethodAnnotation())) {
|
||||||
return TestCaseMethodType.AFTER_TEST;
|
return TestCaseMethodType.AFTER_TEST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user