junit - 使用 AspectJ 记录单元测试结果
问题描述
我正在尝试使用 AspectJ 记录我的测试套件结果。我想在我的代码中的每个 @Test 方法之后“注入”结果标识代码,因此使用以下方法创建了一个方面:
@After("execution(* *(..)) && @annotation(org.junit.Test)")
public void afterTestMethod(JoinPoint joinPoint) {
//identify test result
}
但是,找不到如何检索测试方法结果(通过/失败/跳过)。有什么建议么?
谢谢!
解决方案
A) JUnit 运行监听器
我假设您使用 Maven 或 Gradle 之类的东西构建项目,并将向您展示 JUnit 4 RunListener的 Maven 示例:
在模块test-tools
中,您将添加
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<!--
We build something with the JUnit API, not just run a test,
so scope 'test' is not enough here
-->
<scope>compile</scope>
</dependency>
到您的 POM,然后在src/main/java/...
:
package de.scrum_master.testing;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
public class ResultPrintingRunListener extends RunListener {
@Override
public void testRunStarted(Description description) {
System.out.println("[RunStarted] description = " + description);
}
@Override
public void testRunFinished(Result result) {
System.out.println("[RunFinished] result = " + result);
System.out.println(" run count = " + result.getRunCount());
System.out.println(" failure count = " + result.getFailureCount());
System.out.println(" assumption failure count = " + result.getAssumptionFailureCount());
System.out.println(" ignored count = " + result.getIgnoreCount());
System.out.println(" run time (ms) = " + result.getRunTime());
}
@Override
public void testSuiteStarted(Description description) {
System.out.println("[SuiteStarted] description = " + description);
}
@Override
public void testSuiteFinished(Description description) {
System.out.println("[SuiteFinished] description = " + description);
}
@Override
public void testStarted(Description description) {
System.out.println("[Started] description = " + description);
}
@Override
public void testFinished(Description description) {
System.out.println("[Finished] description = " + description);
}
@Override
public void testFailure(Failure failure) {
System.out.println("[Failure] failure = " + failure);
}
@Override
public void testAssumptionFailure(Failure failure) {
System.out.println("[AssumptionFailure] failure = " + failure);
}
@Override
public void testIgnored(Description description) {
System.out.println("[Ignored] description = " + description);
}
}
您可以将 API 调用放在适当的位置,而不是记录日志。
在您的应用程序测试模块中,您可以将该test-tools
模块添加为依赖项,然后像这样配置您的 Maven Surefire/Failsafe 插件:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<properties>
<property>
<name>listener</name>
<value>de.scrum_master.testing.ResultPrintingRunListener</value>
</property>
</properties>
</configuration>
</plugin>
如果你在 Maven 中运行这样的测试,......
package de.scrum_master.agent.aspect;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
public class MyTest {
@Test
public void one() {
Assert.assertEquals("xander", "Alexander".substring(3));
}
@Test
public void two() {
Assert.assertEquals("Alex", "Alexander".substring(3));
}
@Test
public void three() {
Assert.assertEquals(11, 1 / 0);
}
@Test
@Ignore
public void four() {
Assert.assertNull(null);
}
}
... Maven 打印:
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[RunStarted] description = null
[INFO] Running de.scrum_master.agent.aspect.MyTest
[SuiteStarted] description = de.scrum_master.agent.aspect.MyTest
[Started] description = one(de.scrum_master.agent.aspect.MyTest)
[Finished] description = one(de.scrum_master.agent.aspect.MyTest)
[Started] description = two(de.scrum_master.agent.aspect.MyTest)
[Failure] failure = two(de.scrum_master.agent.aspect.MyTest): expected:<[Alex]> but was:<[xander]>
[Finished] description = two(de.scrum_master.agent.aspect.MyTest)
[Ignored] description = four(de.scrum_master.agent.aspect.MyTest)
[Started] description = three(de.scrum_master.agent.aspect.MyTest)
[Failure] failure = three(de.scrum_master.agent.aspect.MyTest): / by zero
[Finished] description = three(de.scrum_master.agent.aspect.MyTest)
[SuiteFinished] description = de.scrum_master.agent.aspect.MyTest
[ERROR] Tests run: 4, Failures: 1, Errors: 1, Skipped: 1, Time elapsed: 0.11 s <<< FAILURE! - in de.scrum_master.agent.aspect.MyTest
[ERROR] de.scrum_master.agent.aspect.MyTest.two Time elapsed: 0.007 s <<< FAILURE!
org.junit.ComparisonFailure: expected:<[Alex]> but was:<[xander]>
at de.scrum_master.agent.aspect.MyTest.two(MyTest.java:31)
[ERROR] de.scrum_master.agent.aspect.MyTest.three Time elapsed: 0.001 s <<< ERROR!
java.lang.ArithmeticException: / by zero
at de.scrum_master.agent.aspect.MyTest.three(MyTest.java:36)
[RunFinished] result = org.junit.runner.Result@79be0360
run count = 3
failure count = 2
assumption failure count = 0
ignored count = 1
run time (ms) = 0
JUnit 测试观察者规则
如果您更喜欢在测试中独立于特定 JUnit 运行器的东西,请使用 JUnit TestWatcher创建这样的基类:
package de.scrum_master.agent.aspect;
import org.junit.Rule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
public class TestBase {
@Rule(order = Integer.MIN_VALUE)
public TestWatcher testWatcher = new TestWatcher() {
@Override
protected void failed(Throwable e, Description description) {
System.out.println("[TestWatcher failed] description = " + description +", e = " + e);
}
@Override
protected void succeeded(Description description) {
System.out.println("[TestWatcher succeeded] description = " + description);
}
};
}
extends TestBase
然后直接或间接地制作所有测试类。如果您运行测试,例如从 IDE 运行,您会看到(缩短的输出,仅测试观察程序日志):
[TestWatcher succeeded] description = one(de.scrum_master.agent.aspect.MyTest)
[TestWatcher failed] description = two(de.scrum_master.agent.aspect.MyTest), e = org.junit.ComparisonFailure: expected:<[Alex]> but was:<[xander]>
[TestWatcher failed] description = three(de.scrum_master.agent.aspect.MyTest), e = java.lang.ArithmeticException: / by zero
你看,测试观察器中的事件更少,例如不报告被忽略的测试。
尽管我很喜欢 AspectJ(这就是我发现这个问题的方式),但我认为您应该首先尝试适当地配置 JUnit,并且可能会对它感到满意。如果出于某种原因 - 请解释一下,如果是这样 - 您仍然坚持使用 AOP 解决方案,请告诉我。
(C) 直接拦截 JUnit 测试
因为你坚持:
package de.scrum_master.aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class TestResultInterceptor {
@AfterReturning(value = "execution(* *(..)) && @annotation(org.junit.Test)", returning = "result")
public void allMethods(JoinPoint joinPoint, Object result) {
System.out.println(joinPoint + " -> PASSED");
}
@AfterThrowing(value = "execution(* *(..)) && @annotation(org.junit.Test)", throwing = "throwable")
public void allMethods(JoinPoint joinPoint, Throwable throwable) {
System.out.println(joinPoint + " -> FAILED: " + throwable);
}
}
在我的 IDE 中运行上面的 JUnit 测试时,控制台日志是:
execution(void de.scrum_master.testing.MyTest.one()) -> PASSED
execution(void de.scrum_master.testing.MyTest.two()) -> FAILED: org.junit.ComparisonFailure: expected:<[Alex]> but was:<[xander]>
execution(void de.scrum_master.testing.MyTest.three()) -> FAILED: java.lang.ArithmeticException: / by zero
我想你可以从这里拿走。
推荐阅读
- laravel - Laravel:如何仅使用一个存储库实现存储库设计模式?
- r - 计算小时桶的平均值
- python-3.x - 优化3级嵌套for循环以有效输出结果
- linux - 如何在 i3-gap 上获得 Fn + Home/End 绑定工作?
- powerbi - Power BI Embedded 报表成本
- jquery - 为什么我的 Datepicker 不更新输入字段的值?
- android-studio - 离子角电容器应用程序“在Android Studio中运行时无法确定任务':app:compileDebugJavaWithJavac'的依赖关系
- javascript - 如何从 API 中循环遍历数组,其中某些属性的值为 null,而不修改它
- amazon-web-services - 使用查询和搜索条件生成 AWS `Logs Insights` URL
- android - 无法在android模拟器上运行应用程序,应用程序不断停止