使用 AspectJ 记录单元测试结果
Logging of unit test result using AspectJ
我正在尝试使用 AspectJ 记录我的测试套件结果。我想 'inject' 在我的代码中的每个 @Test 方法之后的结果标识代码,因此使用以下方法创建了一个方面:
@After("execution(* *(..)) && @annotation(org.junit.Test)")
public void afterTestMethod(JoinPoint joinPoint) {
//identify test result
}
但是,找不到如何检索测试方法结果 (passed/failed/skipped)。
有什么建议吗?
谢谢!
A) JUnit 运行 监听器
我假设您使用 Maven 或 Gradle 之类的东西构建您的项目,并将向您展示 JUnit 4 的 Maven 示例 RunListener:
在模块 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 运行ner 的东西,请使用 JUnit TestWatcher:
创建一个像这样的基础 class
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);
}
};
}
然后直接或间接地进行所有测试classes 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
我想你可以从这里开始。
我正在尝试使用 AspectJ 记录我的测试套件结果。我想 'inject' 在我的代码中的每个 @Test 方法之后的结果标识代码,因此使用以下方法创建了一个方面:
@After("execution(* *(..)) && @annotation(org.junit.Test)")
public void afterTestMethod(JoinPoint joinPoint) {
//identify test result
}
但是,找不到如何检索测试方法结果 (passed/failed/skipped)。 有什么建议吗?
谢谢!
A) JUnit 运行 监听器
我假设您使用 Maven 或 Gradle 之类的东西构建您的项目,并将向您展示 JUnit 4 的 Maven 示例 RunListener:
在模块 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 运行ner 的东西,请使用 JUnit TestWatcher:
创建一个像这样的基础 classpackage 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);
}
};
}
然后直接或间接地进行所有测试classes 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
我想你可以从这里开始。