GithubHelp home page GithubHelp logo

alibaba / testable-mock Goto Github PK

View Code? Open in Web Editor NEW
1.8K 37.0 307.0 1.53 MB

换种思路写Mock,让单元测试更简单

Home Page: https://alibaba.github.io/testable-mock/

License: MIT License

Java 99.12% Shell 0.88%
unittest mock

testable-mock's Introduction

TestableMock

换种思路写Mock,让单元测试更简单。

无需初始化,不挑服务框架,甭管要换的是私有方法、静态方法、构造方法还是其他任何类的任何方法,也甭管要换的对象是怎么创建的。写好Mock定义,加个@MockInvoke注解,一切统统搞定。

阅读这里了解更多故事。

0.7版本已发布,从0.6.x升级到0.7.x版本请参考版本升级指南


项目维护说明

由于当前并行项目较多,此项目暂时转入维护阶段,在此期间TestableMock会继续提供不定期的版本更新。

如果有遇到其他任何使用问题和建议,请直接在Issues中提出,也可通过Pull Request提交您的代码,我们将尽快回复并处理。

目录结构

|-- testable-parent       ➜ 提供各子模块的公共父pom文件
|-- testable-all          ➜ 依赖聚合,便于一次性引用所有子模块功能
|-- testable-processor    ➜ 编译期代码预处理模块,提供测试辅助功能
|-- testable-agent        ➜ JavaAgent模块,提供Mock测试相关功能
|-- testable-core         ➜ 基础功能模块,提供Mock相关注解和工具类
|-- testable-maven-plugin ➜ Maven插件模块,用于简化JavaAgent注入
|-- tool                  ➜ 项目开发过程中的工具脚本
|-- demo
|   |-- java-demo         ➜ Java语言的示例代码
|   |-- kotlin-demo       ➜ Kotlin语言的示例代码
|   |-- android-demo      ➜ Android项目的示例代码
|   `-- spock-demo        ➜ Spock测试框架的示例代码
`-- docs                  ➜ 项目使用文档

构建项目

主项目使用JDK 1.6+和Maven 3+版本构建,其中demo子项目需要JDK 1.8+版本。

mvn clean install

本地生成文档

docsify serve docs

TestableMock文档使用docsify工具生成,构建前请安装nodejs运行时,并使用npm install -g docsify命令安装文档生成工具。

testable-mock's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

testable-mock's Issues

公共方法、私有方法都无法mock

@DaTa
public class IcoHome {
private String no;
private String name;
private Integer age;
}

public class IcoHomeMethod {
public IcoHome pubMethod(String no) {
return priMethod(no);
}
private IcoHome priMethod(String no) {
return null;
}
}

@slf4j
public class IcoHomeServiceTests {

private IcoHomeMethod method = new IcoHomeMethod();

@MockMethod(targetMethod = "pubMethod")
public IcoHome mockPubMethod(IcoHomeMethod self, String no) {
    IcoHome icoHome = new IcoHome();
    icoHome.setNo("1");
    icoHome.setName("mjh");
    icoHome.setAge(8);
    return icoHome;
}

@MockMethod(targetMethod = "priMethod")
private IcoHome mockPriMethod(IcoHomeMethod self, String no) {
    IcoHome icoHome = new IcoHome();
    icoHome.setNo("2");
    icoHome.setName("mjh");
    icoHome.setAge(9);
    return icoHome;
}

@Test
public void getByNoTest() {
    IcoHome byNo = method.pubMethod("3");
    log.info("{}", byNo);
    verify("mockPubMethod").withTimes(1);
    verify("mockPriMethod").withTimes(1);

}

}

com.alibaba.testable.core.error.VerifyFailedError:
Expected times: 1
Actual times: 0

at com.alibaba.testable.core.matcher.InvokeVerifier.withTimes(InvokeVerifier.java:98)

增加Mock方法正确性检查功能,提高代码重构安全性

两种方案:

  1. 扩展testable-processor能力,增加对Mock目标方法是否存在的编译期检查
  2. 扩展testable-agent能力,增加对Mock目标方法是否存在的运行期检查

方案一能够更早发现错误,但由于编译期检查局限性,无法动态加载系统和三方类,只能对被测类成员方法的Mock进行验证。
方案二需要在测试运行时才能发现Mock目标缺失,但能够比较全面的检查目标方法的存在性。即从出错信息上明确区分“Mock目标不存在”的错误和“测试结果不正确”的错误,确保在代码重构导致Mock目标更名时能够及时发现。
综合考虑计划采用方案二,在v0.5重构版本完成后实施。

idea中build gradle 项目失败,指定类找不到,URLClassLoader路径有问题

代码修改如下

public EnablePrivateAccessTranslator(Symbol.ClassSymbol clazz, TestableContext cx) {
String pkgName = ((Symbol.PackageSymbol) clazz.owner).fullname.toString();
String testClassName = clazz.getSimpleName().toString();
String sourceClass = testClassName.substring(0, testClassName.length() - ConstPool.TEST_POSTFIX.length());
this.privateAccessStatementGenerator = new PrivateAccessStatementGenerator(cx);
this.sourceClassName = cx.names.fromString(sourceClass);
try {
Class<?> cls = null;
String sourceClassFullName = pkgName + "." + sourceClass;
try {
cls = Class.forName(sourceClassFullName);
} catch (ClassNotFoundException e) {
if (System.getProperty(IDEA_PATHS_SELECTOR) != null) {
// fit for intellij 2020.3+
String sourceFileWrapperString = clazz.sourcefile.toString();
String sourceFilePath = sourceFileWrapperString.substring(
sourceFileWrapperString.lastIndexOf("[") + 1, sourceFileWrapperString.indexOf("]"));
int indexOfSrc = sourceFilePath.lastIndexOf(File.separator + "src" + File.separator);
String targetFolderPath = StringUtil.fitPathString(sourceFilePath.substring(0, indexOfSrc) +
"/build/classes/java/main/");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:/" + targetFolderPath)});
cls = urlClassLoader.loadClass(sourceClassFullName);
} else {
// fit for gradle build
String path = StringUtil.fitPathString("file:"
+ System.getProperty(USER_DIR) + "/build/classes/java/main/");
cls = new URLClassLoader(new URL[]{new URL(path)}).loadClass(sourceClassFullName);
}
}
if (cls == null) {
System.err.println("Failed to load source class: " + sourceClassFullName);
return;
}
}

通过 @Reference引用Dubbo Facade怎么注入到被测试类中

文档里有说明使用@autowire注入的可用通过PrivateAccessor.set(被测对象, "私有字段名", 新的值)来注入。
像引用外部Dubbo接口,Facade包里不会提供接口的实现类,所以接口没法new一个新的值,这种情况怎么处理。

如下,被测试类是CollectBizImpl:

@component
@slf4j
public class CollectBizImpl implements CollectBiz {
/**
* 引用dubbo接口
*
*/
@reference
private CollectRouterFacade collectRouterFacade;

@Override
public Result initiateCollect(CollectReqDTO reqDTO) {


	// do something 

	XxxResultDTO resultDTO = collectRouterFacade.collect(xxx,xxx);

	// resolve resultDTO

	return result; 
}

}

静态方法Mock时会报 java.lang.VerifyError: Bad local variable type

JDK:1.8.0_u271

Internal Error occurred.
org.junit.platform.commons.JUnitException: TestEngine with ID 'junit-vintage' failed to discover tests
at org.junit.platform.launcher.core.DefaultLauncher.discoverEngineRoot(DefaultLauncher.java:189)
at org.junit.platform.launcher.core.DefaultLauncher.discoverRoot(DefaultLauncher.java:168)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.VerifyError: Bad local variable type
Exception Details:
Location:
xxxxxx
Reason:
Type top (current frame, locals[1]) is not assignable to reference type
Current Frame:
bci: @6
flags: { }
locals: { 'xxxxxx' }
stack: { '[Ljava/lang/Object;', '[Ljava/lang/Object;', integer }
Bytecode:
xxxxxxxxxxx

at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetPublicMethods(Class.java:2902)
at java.lang.Class.getMethods(Class.java:1615)
at org.junit.platform.commons.util.ReflectionUtils.getDefaultMethods(ReflectionUtils.java:1440)
at org.junit.platform.commons.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:1413)
at org.junit.platform.commons.util.ReflectionUtils.findAllMethodsInHierarchy(ReflectionUtils.java:1355)
at org.junit.platform.commons.util.ReflectionUtils.findMethods(ReflectionUtils.java:1339)
at org.junit.platform.commons.util.ReflectionUtils.findMethods(ReflectionUtils.java:1325)
at org.junit.vintage.engine.descriptor.TestSourceProvider.lambda$findMethod$1(TestSourceProvider.java:64)
at java.util.HashMap.computeIfAbsent(HashMap.java:1127)
at java.util.Collections$SynchronizedMap.computeIfAbsent(Collections.java:2674)
at org.junit.vintage.engine.descriptor.TestSourceProvider.findMethod(TestSourceProvider.java:64)
at org.junit.vintage.engine.descriptor.TestSourceProvider.findTestSource(TestSourceProvider.java:45)
at org.junit.vintage.engine.discovery.RunnerTestDescriptorPostProcessor.addChildrenRecursively(RunnerTestDescriptorPostProcessor.java:63)
at org.junit.vintage.engine.discovery.RunnerTestDescriptorPostProcessor.applyFiltersAndCreateDescendants(RunnerTestDescriptorPostProcessor.java:45)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at org.junit.vintage.engine.discovery.VintageDiscoverer.discover(VintageDiscoverer.java:50)
at org.junit.vintage.engine.VintageTestEngine.discover(VintageTestEngine.java:63)
at org.junit.platform.launcher.core.DefaultLauncher.discoverEngineRoot(DefaultLauncher.java:181)
... 6 more

私有方法参数包含List类型,执行无效

1.定义了一个私有打印集合方法
private void printList(List<String> str) { System.out.println(str); }
2.在测试类里面传入list并调用,发现控制台没有任何输出,换成单个字符串是可以的

NoClassDefFoundError

java.lang.NoClassDefFoundError: com/alibaba/testable/core/matcher/InvokeVerifier

at com.alibaba.testable.demo.DemoInheritTest.should_able_to_mock_call_sub_object_method_by_parent_object(DemoInheritTest.java:54)

@MockMethod中使用了全局变量时,启动报错:java.lang.VerifyError: Bad type on operand stack

@MockMethod中使用了全局变量时,启动报错:java.lang.VerifyError: Bad type on operand stack,将全局变量使用static修饰后就正常了,全局变量一定要static修饰的原因是什么呢?文档没有提到这点,相关代码如下

// 1. 单测类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringCloudApplication.class)
@ActiveProfiles(profiles = "dev")
@MockWith(diagnose = MockDiagnose.ENABLE) // 可开启mock启动调试
public class DependentServiceImplTest {

    @Autowired
    private DependentServiceImpl dependentService;

    //static String uid = "111";// 正常
    String uid = "111";// 异常 java.lang.VerifyError: Bad type on operand stack

    @Test
    public void testMockReturn() {
        System.out.println(dependentService.submitGuess("123456"));
    }

    @MockMethod
    private String substring(String self, int i, int j) {
        return uid;
        //return "substring";
    }
}

// 2. 接口类
public interface DependentService {

    Integer submitGuess(String userId);
}

// 3. 实现类
@Component
public class DependentServiceImpl implements DependentService {

    @Override
    public Integer submitGuess(String userId) {
        System.out.println("substring():" + userId.substring(0, 2));
        System.out.println("length():" + userId.length());
        // 查询DB
        if (userHadJoin(userId)) {
            return 0;
        }
        insertJoin(userId);
        return 1;
    }

    private Integer insertJoin(String userId) {
        //插入数据
        return 1;
    }

    private boolean userHadJoin(String userId) {
        // 查询DB
        return false;
    }
}

完整异常信息如下

[ERROR] Attempt to access non-static member in mock method com/example/testablemock/impl/DependentServiceImplTest:substring

java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    com/example/testablemock/impl/DependentServiceImplTest.substring(Ljava/lang/String;II)Ljava/lang/String; @28: getfield
  Reason:
    Type 'java/lang/String' (current frame, stack[0]) is not assignable to 'com/example/testablemock/impl/DependentServiceImplTest'
  Current Frame:
    bci: @28
    flags: { }
    locals: { 'java/lang/String', integer, integer }
    stack: { 'java/lang/String' }
  Bytecode:
    0x0000000: 06bd 0004 5903 2a53 5904 1bb8 0060 5359
    0x0000010: 051c b800 6053 0304 b800 662a b400 26b0
    0x0000020:

mock的方法体内定义了超过一个的变量时,如果使用switch会报错

@MockMethod(targetClass = SetOperations.class)
private Boolean isMember(K key,Object o) {
String testName = (String) TestableTool.MOCK_CONTEXT.get("testName");
String a = "";
switch (testName) {
case "场景1" :
case "场景2" :
case "场景3" :
return true;
default:
return false;
}
}

Caused by: java.lang.ClassFormatError: StackMapTable format error: bad type array size in method isMember

英文文档的发布

想把这个工具推荐给部门的人看, 能不能发布一下英文版的文档?

java demo error

Java demo DemoPrivateAccessTest类 26行
DemoPrivateAccess.staticCount = 2;
私有成员变量写法编译不了,不能运行。

SpringBoot项目中Gradle没法引入

错误信息:
Could not find method testAnnotationProcessor() for arguments [com.alibaba.testable:testable-processor:0.4.9] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
环境版本:JDK1.8,Gradle 6.7 和IDEA自带的Gradle都尝试了。IDEA版本版本:2020.3.2版本
builder.gradle 配置信息如下(部分):
buildscript {

ext {
    springBootVersion = '2.0.3.RELEASE'
}

repositories {
    mavenLocal()

    maven { url "https://repo.spring.io/plugins-release" }
}
dependencies {
    classpath("io.spring.gradle:propdeps-plugin:0.0.8")
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    testImplementation('com.alibaba.testable:testable-all:0.4.9')
    testAnnotationProcessor('com.alibaba.testable:testable-processor:0.4.9')
}
test {
    jvmArgs "-javaagent:${classpath.find { it.name.contains("testable-agent") }.absolutePath}"
}

}

@MockMethod(targetClass = SetOperations.class)报错,不用targetClass写在参数里就没报错

java.lang.VerifyError: Operand stack underflow
Exception Details:
Location:
//service/impl/.(Ljava/lang/Long;)Ljava/util/List; @22: getfield
Reason:
Attempt to pop empty stack.
Current Frame:
bci: @22
flags: { }
locals: { '
//service/impl/***', 'java/lang/Long', 'java/util/List' }
stack: { }
Bytecode:
0x0000000: 2ab4 002d 2bb9 0032 0200 4d2c b800 3899
0x0000010: 0007 b800 3eb0 b400 42b6 0048 124a b800
0x0000020: 504e 2cb9 0054 0100 3a04 1904 b900 5c01
0x0000030: 0099 0038 1904 b900 6001 00c0 0062 3a05
0x0000040: 2d19 05b6 0066 b600 6cb9 0070 0200 9900
0x0000050: 0f19 0504 b800 74b6 0078 a700 0c19 0503
0x0000060: b800 74b6 0078 a7ff c42c ba00 8b00 00b8
0x0000070: 0091 b900 9501 00b9 0099 0200 2cb0
Stackmap Table:
append_frame(@22,Object[#64])
append_frame(@42,Object[#86],Object[#88])
append_frame(@93,Object[#98])
chop_frame(@102,1)
chop_frame(@105,1)

调用链较长时mock失败

调用链较长时mock失败,麻烦看一下,辛苦了,多谢:

package com.alibaba.testable.demo;

public class A {
    public String a1() {
        return new B().b1();
    }

    public String a2() {
        return new B().b2();
    }
}
package com.alibaba.testable.demo;

public class B {
    public String b1() {
        return "real";
    }

    public String b2() {
        return b1();
    }
}
package com.alibaba.testable.demo;

import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.core.annotation.MockWith;
import com.alibaba.testable.core.model.MockDiagnose;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

@MockWith(diagnose = MockDiagnose.ENABLE)
class ATest {

    @MockMethod
    String b1(B b) {
        return "mock";
    }

    @Test
    void should_mock_when_short_call_chain() {
        assertEquals("mock", new A().a1());//passed
    }

    @Test
    void should_mock_when_long_call_chain() {
        assertEquals("mock", new A().a2());//failed
    }

}

gradle 项目,idea中build失败,类找不到,路径解析/src/不符合windows规范

[INFO] Testable processor initialized
java.lang.StringIndexOutOfBoundsException: begin 0, end -1, length 98
at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3319)
at java.base/java.lang.String.substring(String.java:1874)
at com.alibaba.testable.processor.translator.EnablePrivateAccessTranslator.(EnablePrivateAccessTranslator.java:64)
at com.alibaba.testable.processor.EnablePrivateAccessProcessor.processClassElement(EnablePrivateAccessProcessor.java:83)
at com.alibaba.testable.processor.EnablePrivateAccessProcessor.process(EnablePrivateAccessProcessor.java:57)
at org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor.process(DelegatingProcessor.java:62)
at org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessor.process(NonIncrementalProcessor.java:45)
at org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor.process(DelegatingProcessor.java:62)
at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor.access$401(TimeTrackingProcessor.java:37)
at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$5.create(TimeTrackingProcessor.java:99)
at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$5.create(TimeTrackingProcessor.java:96)
at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor.track(TimeTrackingProcessor.java:117)
at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor.process(TimeTrackingProcessor.java:96)
at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:972)
at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:888)
at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1214)
at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1326)
at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1258)
at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:936)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:104)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:147)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:100)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:94)
at org.gradle.internal.compiler.java.IncrementalCompileTask.call(IncrementalCompileTask.java:74)
at org.gradle.api.internal.tasks.compile.AnnotationProcessingCompileTask.call(AnnotationProcessingCompileTask.java:93)
at org.gradle.api.internal.tasks.compile.ResourceCleaningCompilationTask.call(ResourceCleaningCompilationTask.java:57)
at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:55)
at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:40)
at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.delegateAndHandleErrors(NormalizingJavaCompiler.java:97)
at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:51)
at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:37)
at org.gradle.api.internal.tasks.compile.AnnotationProcessorDiscoveringCompiler.execute(AnnotationProcessorDiscoveringCompiler.java:51)
at org.gradle.api.internal.tasks.compile.AnnotationProcessorDiscoveringCompiler.execute(AnnotationProcessorDiscoveringCompiler.java:37)
at org.gradle.api.internal.tasks.compile.ModuleApplicationNameWritingCompiler.execute(ModuleApplicationNameWritingCompiler.java:46)
at org.gradle.api.internal.tasks.compile.ModuleApplicationNameWritingCompiler.execute(ModuleApplicationNameWritingCompiler.java:36)
at org.gradle.api.internal.tasks.compile.CleaningJavaCompiler.execute(CleaningJavaCompiler.java:53)
at org.gradle.api.internal.tasks.compile.incremental.IncrementalCompilerFactory.lambda$createRebuildAllCompiler$0(IncrementalCompilerFactory.java:98)
at org.gradle.api.internal.tasks.compile.incremental.IncrementalResultStoringCompiler.execute(IncrementalResultStoringCompiler.java:60)
at org.gradle.api.internal.tasks.compile.incremental.IncrementalResultStoringCompiler.execute(IncrementalResultStoringCompiler.java:44)
at org.gradle.api.internal.tasks.compile.CompileJavaBuildOperationReportingCompiler$2.call(CompileJavaBuildOperationReportingCompiler.java:59)
at org.gradle.api.internal.tasks.compile.CompileJavaBuildOperationReportingCompiler$2.call(CompileJavaBuildOperationReportingCompiler.java:51)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:409)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:399)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:157)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:242)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:150)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:94)
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
at org.gradle.api.internal.tasks.compile.CompileJavaBuildOperationReportingCompiler.execute(CompileJavaBuildOperationReportingCompiler.java:51)
at org.gradle.api.tasks.compile.JavaCompile.performCompilation(JavaCompile.java:293)
at org.gradle.api.tasks.compile.JavaCompile.performIncrementalCompilation(JavaCompile.java:203)
at org.gradle.api.tasks.compile.JavaCompile.compile(JavaCompile.java:183)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
at org.gradle.api.internal.project.taskfactory.IncrementalInputsTaskAction.doExecute(IncrementalInputsTaskAction.java:32)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:42)
at org.gradle.api.internal.project.taskfactory.AbstractIncrementalTaskAction.execute(AbstractIncrementalTaskAction.java:25)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.run(ExecuteActionsTaskExecuter.java:569)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:395)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:387)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:157)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:242)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:150)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:84)
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:554)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:537)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$300(ExecuteActionsTaskExecuter.java:108)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.executeWithPreviousOutputFiles(ExecuteActionsTaskExecuter.java:278)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:267)
at org.gradle.internal.execution.steps.ExecuteStep.lambda$execute$0(ExecuteStep.java:32)
at java.base/java.util.Optional.map(Optional.java:265)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:32)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:26)
at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:67)
at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:36)
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:49)
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:34)
at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:43)
at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:73)
at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:54)
at org.gradle.internal.execution.steps.CatchExceptionStep.execute(CatchExceptionStep.java:34)
at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:44)
at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:54)
at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:38)
at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:49)
at org.gradle.internal.execution.steps.CacheStep.executeWithoutCache(CacheStep.java:159)
at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:72)
at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:43)
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:44)
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:33)
at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:38)
at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:24)
at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:92)
at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:85)
at java.base/java.util.Optional.map(Optional.java:265)
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:55)
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:39)
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:76)
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:37)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:36)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:26)
at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:94)
at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:49)
at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:79)
at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:53)
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:74)
at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$execute$2(SkipEmptyWorkStep.java:78)
at java.base/java.util.Optional.orElseGet(Optional.java:369)
at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:78)
at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:34)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:39)
at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:40)
at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:28)
at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:33)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:194)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:186)
at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:114)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:62)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:409)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:399)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:157)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:242)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:150)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:94)
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:41)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:356)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
at java.base/java.lang.Thread.run(Thread.java:834)

if (System.getProperty(IDEA_PATHS_SELECTOR) != null) {
// fit for intellij 2020.3+
String sourceFileWrapperString = clazz.sourcefile.toString();
String sourceFilePath = sourceFileWrapperString.substring(
sourceFileWrapperString.lastIndexOf("[") + 1, sourceFileWrapperString.indexOf("]"));
String targetFolderPath = sourceFilePath.substring(0, sourceFilePath.lastIndexOf("/src/")) +
"/target/classes/";
cls = new URLClassLoader(new URL[] {new URL(targetFolderPath)}).loadClass(sourceClassFullName);
}

不支持JNI

public class JniUtil {
    static {
        System.loadLibrary("native-lib");
    }


    public native int add(int a, int b);

    public int getAdd() {
        return add(2, 2);
    }
}
public class JniUtilTest {
    private JniUtil mJniUtil = new JniUtil();

    @MockMethod(targetClass = System.class)
    private void loadLibrary(String libname) {
        System.err.println("loadLibrary " + libname);
    }

    @MockMethod(targetClass = JniUtil.class)
    private int add(int a, int b) {
        return a + b;
    }

    @Test
    public void test_add() {
        assertEquals(4, mJniUtil.getAdd());
    }
}

出现以下错误
[DIAGNOSE] Handling test class com/xxx/xxx/JniUtilTest
[DIAGNOSE] Handling source class com/xxx/xxx/JniUtil
[DIAGNOSE] Found 2 mock methods
[DIAGNOSE] Handling method
[DIAGNOSE] Handling method add
[WARN] Failed to transform class com/xxx/xxx/JniUtil
[DIAGNOSE] java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0

MockMethod无效,无法对方法进行mock

@EnablePrivateAccess
public class OrderServiceImplTestWithAlibaba {
private OrderServiceImpl testInstance = new OrderServiceImpl();

@MockMethod(targetClass = com.ctrip.car.customerservice.utils.QConfigUtils.class, targetMethod = "getValue")
public String getValue(String key) {
    String json = "[{\"tagName\":\"\",\"key\":\"安心行\",\"type\":\"equal\",\"template\":\"<span>安心行说明文案安心行说明文案安心行说明文案111<a href='http://www.baidu.com' target='_blank'>查看conf</a></span>\"}]";
    return json;
}
@Test
public void tetGetTagDescription(){
    List<String> tags = Lists.newArrayList("无忧租","安心行");
    PrivateAccessor.set(testInstance, "qConfigUtils" ,new com.ctrip.car.customerservice.utils.QConfigUtils());
    List<TagDescription> list = PrivateAccessor.invoke(testInstance, "getTagDescription", tags);
    assertNotNull(list);
}

}

单测运行的时候,打了断点没有执行上面MockMethod的方法,而是执行了下面这个原有的实现
public String getValue(String key) {
return MapUtils.getString(paramsMap, key);
}

启动demo报错

能给给出更多的文档么,现在这个文档有点儿不全,在idea2020.3中跑不起来。
错误:java: count 在 com.alibaba.testable.demo.DemoPrivateAccess 中是 private 访问控制

Test case failed using testable-mock with junit:junit:4.+

I am using the java-demo in this project. And changed the testImplementation to junit:junit:4.+.
like:
dependencies { //testImplementation('org.junit.jupiter:junit-jupiter:5.6.2') testImplementation 'junit:junit:4.+' ...... }

Then changed the all need import of DemoMockTest, and run the result is failed:
expected:<[mock_]something> but was:<[]something>
Expected :mock_something
Actual :something

org.junit.ComparisonFailure: expected:<[mock_]something> but was:<[]something>
at com.alibaba.testable.demo.DemoMockTest.should_able_to_mock_new_object(DemoMockTest.java:75)

Who can tell me the reason? Thanks.

评估放宽测试类名称约定的可行性

TestableMock的实现中,由于被测类需要访问定义在测试类中的Mock方法,为了便于处理,默认约定了测试类名称为被测类名+Test。虽然绝大多数Java项目均符合此约定,但依然存在特殊场景。

基于实用性考虑,需提供额外机制支持非标准结构项目的测试Mock。

0.4.7 java-demo gradle 编译报错

我的gradle版本是4.10.2
我下载了0.4.7 java-demo 的代码在本地跑demo中的用例时Task :compileTestJava FAILED。

Task :compileTestJava FAILED
D:\workspace\testable-mock-0.4.7\demo\java-demo\src\test\java\com\alibaba\testable\demo\DemoMatcherTest.java:13: ����: ����GBK�IJ���ӳ���ַ�

  • 演示Mock方法调用校验�?
    ^
    [INFO] Testable processor initialized
    java.lang.IllegalArgumentException: character to be escaped is missing
    at java.util.regex.Matcher.appendReplacement(Matcher.java:809)
    at java.util.regex.Matcher.replaceAll(Matcher.java:955)
    at java.lang.String.replaceAll(String.java:2223)
    at com.alibaba.testable.processor.util.PathUtil.fitPathString(PathUtil.java:19)
    at com.alibaba.testable.processor.translator.EnablePrivateAccessTranslator.(EnablePrivateAccessTranslator.java:72)
    at com.alibaba.testable.processor.EnablePrivateAccessProcessor.processClassElement(EnablePrivateAccessProcessor.java:83)
    at com.alibaba.testable.processor.EnablePrivateAccessProcessor.process(EnablePrivateAccessProcessor.java:57)
    at org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor.process(DelegatingProcessor.java:62)
    at org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessor.process(NonIncrementalProcessor.java:45)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
    at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170)
    at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:856)
    at com.sun.tools.javac.main.Main.compile(Main.java:523)
    at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
    at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
    at org.gradle.api.internal.tasks.compile.AnnotationProcessingCompileTask.call(AnnotationProcessingCompileTask.java:89)
    at org.gradle.api.internal.tasks.compile.ResourceCleaningCompilationTask.call(ResourceCleaningCompilationTask.java:57)
    at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:50)
    at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:36)
    at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.delegateAndHandleErrors(NormalizingJavaCompiler.java:100)
    at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:52)
    at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:38)
    at org.gradle.api.internal.tasks.compile.AnnotationProcessorDiscoveringCompiler.execute(AnnotationProcessorDiscoveringCompiler.java:49)
    at org.gradle.api.internal.tasks.compile.AnnotationProcessorDiscoveringCompiler.execute(AnnotationProcessorDiscoveringCompiler.java:35)
    at org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport.execute(CleaningJavaCompilerSupport.java:39)
    at org.gradle.api.internal.tasks.compile.incremental.IncrementalCompilerFactory$2.execute(IncrementalCompilerFactory.java:110)
    at org.gradle.api.internal.tasks.compile.incremental.IncrementalCompilerFactory$2.execute(IncrementalCompilerFactory.java:106)
    at org.gradle.api.internal.tasks.compile.incremental.IncrementalResultStoringCompiler.execute(IncrementalResultStoringCompiler.java:59)
    at org.gradle.api.internal.tasks.compile.incremental.IncrementalResultStoringCompiler.execute(IncrementalResultStoringCompiler.java:43)
    at org.gradle.api.tasks.compile.JavaCompile.performCompilation(JavaCompile.java:153)
    at org.gradle.api.tasks.compile.JavaCompile.compile(JavaCompile.java:121)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    at org.gradle.api.internal.project.taskfactory.IncrementalTaskAction.doExecute(IncrementalTaskAction.java:50)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:39)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:26)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:131)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:300)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:292)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:174)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:120)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:99)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59)
    at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101)
    at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91)
    at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.run(EventFiringTaskExecuter.java:51)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:300)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:292)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:174)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:46)
    at org.gradle.execution.taskgraph.LocalTaskInfoExecutor.execute(LocalTaskInfoExecutor.java:42)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:277)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:262)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:135)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:130)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.execute(DefaultTaskPlanExecutor.java:200)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.executeWithWork(DefaultTaskPlanExecutor.java:191)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.run(DefaultTaskPlanExecutor.java:130)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    at java.lang.Thread.run(Thread.java:748)
    D:\workspace\testable-mock-0.4.7\demo\java-demo\src\test\java\com\alibaba\testable\demo\DemoPrivateAccessTest.java:24: ����: privateFunc(List,String,int)������DemoPrivateAccess���private
    assertEquals("abc + hello + 1", demoPrivateAccess.privateFunc(list, "hello", 1));
    ^
    D:\workspace\testable-mock-0.4.7\demo\java-demo\src\test\java\com\alibaba\testable\demo\DemoPrivateAccessTest.java:30: ����: count������DemoPrivateAccess���private
    demoPrivateAccess.count = 2;
    ^
    D:\workspace\testable-mock-0.4.7\demo\java-demo\src\test\java\com\alibaba\testable\demo\DemoPrivateAccessTest.java:31: ����: count������DemoPrivateAccess���private
    assertEquals(new Integer(2), demoPrivateAccess.count);
    ^
    D:\workspace\testable-mock-0.4.7\demo\java-demo\src\test\java\com\alibaba\testable\demo\DemoPrivateAccessTest.java:39: ����: privateStaticFunc(String,int)������DemoPrivateAccess���private
    assertEquals("hello + 1", DemoPrivateAccess.privateStaticFunc("hello", 1));
    ^
    D:\workspace\testable-mock-0.4.7\demo\java-demo\src\test\java\com\alibaba\testable\demo\DemoPrivateAccessTest.java:45: ����: staticCount������DemoPrivateAccess���private
    DemoPrivateAccess.staticCount = 2;
    ^
    D:\workspace\testable-mock-0.4.7\demo\java-demo\src\test\java\com\alibaba\testable\demo\DemoPrivateAccessTest.java:46: ����: staticCount������DemoPrivateAccess���private
    assertEquals(new Integer(2), DemoPrivateAccess.staticCount);
    ^
    D:\workspace\testable-mock-0.4.7\demo\java-demo\src\test\java\com\alibaba\testable\demo\DemoPrivateAccessTest.java:54: ����: �޷�Ϊ���ձ���pi����ֵ
    demoPrivateAccess.pi = 4.13;
    ^
    ע: D:\workspace\testable-mock-0.4.7\demo\java-demo\src\test\java\com\alibaba\testable\demo\DemoTemplateTest.javaʹ����δ�����򲻰�ȫ�IJ�����
    ע: �й���ϸ��Ϣ, ��ʹ�� -Xlint:unchecked ���±��롣
    7 ������
    FAILURE: Build failed with an exception.
  • What went wrong:
    Execution failed for task ':compileTestJava'.

Compilation failed; see the compiler error output for details.

@MockMethod注解支持targetClass参数

当前在参数列表首位添加Mock目标的机制方便了调用被Mock对象中的方法,但也造成了阅读代码时目标信息和参数信息的混淆。

当无需访问Mock目标对象时,可通过@MockMethod注解的targetClass参数指定目标类型,替代参数列表首位的Mock目标指定,能够在一定程度上提高代码的可读性。

增加Mock方法的上下文控制参数注入机制

当同一测试用例的执行过程中包含对同一个被Mock目标方法的多次调用时,当前TestableTool工具类提供的能力依然无法便捷的区分这几次调用并进行差异化处理。考虑增加一种注入额外控制参数到Mock上下文的机制,完善TestableMock对Mock流程控制能力。

方案设计:

// 在测试用例设置控制参数
@Test
public void test() {
    TestableTool.set(“mark”, 1);  // 注入控制参数
    demoInstance.method(123);   // 测试场景1
    ... ...
    TestableTool.set(“mark”, 2);  // 修改控制参数值
    demoInstance.method1(234);   // 测试场景2
    ... ...
}

// 在Mock方法中读取控制参数
@MockMethod
private int demoMethod() {
    if (TestableTool.get(“mark”) == 1) {
        return 321;
    } else {
        return 432;
    }
}

MockMethod 不生效,附MockWith日志

`@MockWith(diagnose = MockDiagnose.ENABLE)
public class AlertManagerImplTest {

private AlertManager alertManager = new AlertManagerImpl();

@MockMethod
private String trim(String self) {
    System.out.println("into trim");
    return "trim_string";
}

@Test
public void testMock(){

    System.out.println("start test");
    alertManager.commonFunc();
    String a = "sdsds ";
    System.out.println("1122 ".trim());
    System.out.println(a.trim());

}

}`

[DIAGNOSE] Handling test class com/xxx/xxx/xx/xxxx/AlertManagerImplTest

start test
1122
sdsds

Process finished with exit code 0

配合Mockito使用,mock被测对象的方法失败

public class HeroServiceImplTest extends BaseTest {

    @InjectMocks
    @Spy
    private HeroServiceImpl heroService;

    @Mock
    private WeatherClient weatherClient;

    @Mock
    private HeroMapper heroMapper;


    @MockMethod(targetClass = HeroServiceImpl.class, targetMethod = "story")
    public String story(String self, Long heroId, String cityCode) {
       return "hahahah";
   }


    @Test
    public void storyTest() {
        //String story = heroService.story(1L, "1024");//**此处未mock成功,仍调用了真实方法**
       //---------------------新增
        String story = heroService.callStory();
        System.out.println(story);
    }
}
@Slf4j
@Service
public class HeroServiceImpl
        extends BaseServiceImpl<HeroMapper, Hero>
        implements HeroService {

    @Autowired
    private HeroMapper heroMapper;

    @Autowired
    private WeatherClient weatherClient;

    @Override
    public String story(Long heroId, String cityCode) {
        Hero hero = getById(heroId);
        if (hero == null) {
            return String.format("heroId为%d不存在", heroId);
        }

        WeatherExample.Response weather = weatherClient.query(cityCode);

        StringBuilder sb = new StringBuilder();
        sb.append("人物:").append(hero.getHeroName()).append("\n");
        sb.append("技能:").append(hero.getSkill()).append("\n");
        sb.append("地点:").append(weather.getCityInfo().getCity()).append("\n");
        sb.append("天气:").append(weather.getData().getYesterday().getNotice());

        return sb.toString();
    }
    //------------------------------新增
   public String callStory() {
        return this.story(1L,"999");
    }
}

使用spring boot,testable版本0.4.9,Mockito版本3.6.0

lambada调用mock不生效

被测类:

    public String process(String source) {
        List<Function<String, String>> processChain = new ArrayList<>();
        processChain.add(this::function1);
        processChain.add(this::function2);
        String temp = source;
        for(Function<String, String> f : processChain) {
            temp = f.apply(temp);
        }
        return temp;
    }

    private String function1(String source) {
        return "*" + source;
    }

    private String function2(String source) {
        return "#" + source;
    }

    public String process2(String source) {
        return function2(function1(source));
    }

test:

    private DemoLambada demoLambada = new DemoLambada();

    @MockMethod(targetClass = DemoLambada.class)
    private String function1(String source) {
        return "mock1" + source;
    }

    @MockMethod(targetClass = DemoLambada.class)
    private String function2(String source) {
        return "mock2" + source;
    }

    // 不通过
    @Test
    public void test_process() {
        Assertions.assertEquals("mock2mock1s", demoLambada.process("s"));
    }

    @Test
    public void test_process2() {
        System.out.println(demoLambada.process2("s"));
        Assertions.assertEquals("mock2mock1s", demoLambada.process2("s"));
    }

java8 lambda '::' 关键字配合链式调用导致报ClassCastException

//一般的数据库表映射类,PO类
public class User {
private String name;

public void setName(String name) {
    this.name = name;
}

public String getName() {
    return this.name;
}

}

//模拟一般的工具类方法
public class NameChanger {
public static String changeName(String originName) {
return originName + "hasChange";
}
}

public class ChangeCenter {

private ChangeCenter(){}

public static ChangeCenter create() {
    return new ChangeCenter();
}

public User convert(Function<String,String> function) {
    String convert = function.apply("mmmmmm");
    User user = new User();
    user.setName(convert);
    return user;
}

}

//lambda使用示例,对应一般的service类
public class DemoLambda {

public String lambda() {
    return com.alibaba.testable.demo.model.ChangeCenter.create().convert(NameChanger::changeName).getName();
}

}

//测试类
@MockWith(diagnose = MockDiagnose.ENABLE)
public class DemoLambdaTest {

DemoLambda demoLambda = new DemoLambda();

@MockMethod
private String getName(User self) {
    return "name_from_test";
}

@Test
public void test() {
    System.out.println(demoLambda.lambda());
}

}

运行该测试用例,会得到如下的错误提示:
[WARN] Failed to transform class com/alibaba/testable/demo/DemoLambda
[DIAGNOSE] java.lang.ClassCastException: org.objectweb.asm.tree.InvokeDynamicInsnNode cannot be cast to org.objectweb.asm.tree.MethodInsnNode

源码追踪是NameChanger::changeName该代码导致com.alibaba.testable.agent.handler.SourceClassHandler#stackEffectOfInvocation该位置处的InvokeDynamicInsnNode被强转成了MethodInsnNode,导致报错。

但是如果DemoLambda代码改成如下:
public String lambda() {
User user= ChangeCenter.create().convert(NameChanger::changeName);
return user.getName();
}
测试则通过!

望回复,感谢!

java demo可以允许,集成到自己的springboot项目运行报错

一月 05, 2021 4:02:36 下午 org.junit.platform.launcher.core.DefaultLauncher handleThrowable
警告: TestEngine with ID 'junit-jupiter' failed to discover tests
java.lang.NoSuchMethodError: org.junit.platform.engine.EngineDiscoveryRequest.getDiscoveryListener()Lorg/junit/platform/engine/EngineDiscoveryListener;
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:88)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.run(EngineDiscoveryRequestResolution.java:82)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.resolve(EngineDiscoveryRequestResolver.java:113)
at org.junit.jupiter.engine.discovery.DiscoverySelectorResolver.resolveSelectors(DiscoverySelectorResolver.java:45)
at org.junit.jupiter.engine.JupiterTestEngine.discover(JupiterTestEngine.java:69)
at org.junit.platform.launcher.core.DefaultLauncher.discoverEngineRoot(DefaultLauncher.java:168)
at org.junit.platform.launcher.core.DefaultLauncher.discoverRoot(DefaultLauncher.java:155)
at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:120)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:52)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

assertj断言字符串报错

项目是java-demo,

org.assertj
assertj-core
3.18.1
test

@EnablePrivateAccess
class DemoPrivateAccessTest {

private DemoPrivateAccess demoPrivateAccess = new DemoPrivateAccess();

@Test
void should_able_to_access_private_method() {
    List<String> list = new ArrayList<String>() {{ add("a"); add("b"); add("c"); }};
    String hello = demoPrivateAccess.privateFunc(list, "hello", 1);//(1)
    System.out.println(hello);
    Assertions.assertThat(demoPrivateAccess.privateFunc(list, "hello", 1)).isEqualTo("abc + hello + 1");//(2)
    assertEquals("abc + hello + 1", demoPrivateAccess.privateFunc(list, "hello", 1));
    assertEquals("abc + hello + 1", PrivateAccessor.invoke(demoPrivateAccess, "privateFunc", list, "hello", 1));
}

===========================
(1)处会报Error:(25, 41) java: privateFunc(java.util.List<java.lang.String>,java.lang.String,int) 在 com.alibaba.testable.demo.DemoPrivateAccess 中是 private 访问控制

(2)处会报Error:(27, 19) java: 对assertThat的引用不明确
org.assertj.core.api.Assertions 中的方法 assertThat(T) 和 org.assertj.core.api.Assertions 中的方法 assertThat(T) 都匹配

被测类方法的mock是否可以不用传实现类的参数?

现在约定为测试类必须要与被测类在相同的包下面,且为名字加Test,这个约定问题不大,但是被测类方法的mock还是要定义一个被测类的参数(其实现的接口都不行),我觉得既然有了命名约定,那么其方法的Mock可以省略。
举例:A接口,A的实现类AImpl, 内部方法am,A的测试类AImplTest,要覆盖方法am,需要定义一个AImpl的参数(定义成A也不生效),这里我觉得定义这个AImpl的参数没有必要,可以省略。

请问如何mock一个打上 @PostConstruct 的方法

我试着这样写,不行。

@Service
public class xxxServiceImpl implements xxxService {
    @PostConstruct
    private void init() {
        initA();
    }

    private void initA() {

    }
}

@MockWith(diagnose = MockDiagnose.ENABLE)
@RunWith(SpringRunner.class)
@SpringBootTest
public class xxxServiceImplTest {
    @Autowired
    private xxxService xxxServiceImpl;

    @MockMethod(targetMethod = "init")
    private void mock_init(xxxServiceImpl self) {
        System.out.println("mock初始化");
    }
}

会报exception

...
  Reason:
    Type 'xxxServiceImpl' (current frame, stack[0]) is not assignable to 'xxxServiceImplTest'
...

self改成xxxServiceImplTest也没用,求指点下😂

[DIAGNOSE] Handling test class xxxServiceImplTest
[FAIL] Attempt to access none-static member in mock method xxxServiceImplTest:mock_init
[DIAGNOSE] Handling source class xxxServiceImpl

demo项目中mock有效,自己项目中mock无效

我在官方的demo项目里面使用mock,mock是生效的。但是在我自己的springboot项目里使用mock,mock没有生效。两个项目中测试用的代码完全一样的,不知道为什么结果不同。

image

image

[ERROR] Failed to find `this` reference in non-static method my/package/MyTestClass:substring

刚刚接触,在看文档做一些简单的验证。但是所有简单的method mock都在测试方法启动前,抛类似标题描述的错误.

加了@MockWith(diagnose = MockDiagnose.ENABLE)之后,在第一行[DIAGNOSE]... 之后就报错。每个mock method都报错,然后还是继续执行test method,只是都没有mock效果。

我的运行环境是Java8,Gradle 4.10。配置是按照文档里配的: https://alibaba.github.io/testable-mock/#/zh-cn/doc/setup

byte[]导致initialization失败

发生问题的case如下
Mono<byte[]> func(byte[] args),没有具体验证是作为入参还是作为返回值时导致的
推测应该是作为泛型时会发生,byte[]直接作为返回值时没有问题

增加PrivateAccessor访问目标有效性检查,提高代码重构安全性

当开发者使用PrivateAccessor工具类访问被测类型的私有字段与方法时,如果原字段与方法在重构中进行了更名,在测试运行时会导致较难发现的潜在错误和异常行为。TestableMock应当将在代码编译期提前进行访问有效性检查,当发现访问目标已经不存在时,让相关测试类编译报错退出,从而确保测试代码健壮性。

mock private方法后,直接调用mock未成功

@EnablePrivateAccess
@MockWith(diagnose = MockDiagnose.ENABLE)
class PrivateClassTest {
    private PrivateClass privateClass = new PrivateClass();


    @MockMethod
    private String privateMethod(PrivateClass self, String str) {
        return "1";
    }

    @Test
    void test01() {

        assertEquals("1", PrivateAccessor.invoke(privateClass,"privateMethod","111"));//==========错误未通过
        assertEquals("1", privateClass.test02("999999"));//通过
    }
}
public class PrivateClass {

    private String privateMethod(String str) {
        return "private method "+str;
    }

    public String test02(String str) {
        return privateMethod(str);
    }

    public static String staticMethod() {
        return "static method";
    }
}

影响其他测试类的运行

被测试类A/B,类A包含类B,类B包含类C

测试类A使用springboottest,未使用testable,未使用其他mock框架
测试类B使用testable,mock了类C,运行正常

测试类A运行时,类B执行类C的方法,抛出了NPE

这个现象出现的时候我一头雾水,反复debug好久找不到原因,直到把测试类B整个注释掉……
公司代码不方便直接贴上来,大概结构如下

@requiredargsconstructor
@service
public class ClassA {
private final ClassB instanceB;
public CompletableFuture query() {
return instanceB.query();
}
}

@requiredargsconstructor
@service
public class ClassB {
private final ClassC instanceC;
public CompletableFuture query() {
return instanceC.readRedis();
}
}

@service
public class ClassC {
public CompletableFuture readRedis() {
return CompletableFuture.completedFuture("");
}
}

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ServiceInitializer.class)
public class ClassATest {
@Autowired
private ClassA instanceA;
@test
public void test() {
instanceA.query();
}
}

public class ClassBTest {
private ClassB instanceB = new ClassB(new ClassC());
@MockMethod
private CompletableFuture readRedis(final ClassC self) {
return CompletableFuture.completedFuture("mocked");
}
@test
public void test() {
instanceB.query();
}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.