并行执行测试时获取顺序日志
Getting sequential logs while executing tests in parallel
我们一直在使用 testng
和 java
来对我们的代码执行集成测试。我们为测试执行实现了 listener 如下:-
public class TestExecutionListener implements IInvokedMethodListener {
@Override
public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
System.out.println("Testing : " + iInvokedMethod.getTestMethod().getMethodName());
}
@Override
public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
System.out.println("Successfully Tested : " + iInvokedMethod.getTestMethod().getMethodName());
}
}
我们的testng.xml定义为:-
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestSuite" verbose="1" parallel="classes" thread-count="10">
<listeners>
<listener class-name="core.TestExecutionListener"/>
</listeners>
<test name="IntegrationTests">
<classes>
<class name="test.SomeTest1"/>
<class name="test.SomeTest2"/>
<class name="test.SomeTest3"/>
<class name="test.SomeTest4"/>
... There are more than 20 classes
</classes>
</test>
</suite>
当我们执行测试时,我们得到的输出如下:
Testing : SomeTest1Method1
Testing : SomeTest2Method2
Testing : SomeTest4Method5
Successfully Tested : SomeTest2Method2
Successfully Tested : SomeTest4Method5
而我们期望的输出是:-
Testing : SomeTest1Method1
Successfully Tested : SomeTest1Method1
Testing : SomeTest2Method2
Successfully Tested : SomeTest2Method2
Testing : SomeTest4Method5
Successfully Tested : SomeTest4Method5
猜测这是因为 xml 中的 parallel="classes"
属性,因为将其更改为 false
可提供所需的输出。但是很明显,与并行执行相比,更改后的执行会消耗更多的时间。
有没有办法并行运行这些测试但仍然按顺序得到这个输出?
首先,我建议您使用 log4j 或类似的记录器工具,它们优雅、模块化并提供许多灵活的选项。这也为以多种方式设计和处理日志记录机制奠定了基础。
如果您有业务需求,需要您分别跟踪每个线程的日志,您应该考虑将这些日志流式传输到不同的文件。 Log4j 类工具确实支持写入多个文件的需求。你可以google搜索,你会得到很多信息。
当您启用并行时,正如您所注意到的,日志中的 beforeInvocation
和 afterInvocation
之间会有一些时间,并且时间差异因测试而异,因此输出交错。
如果您想要的是彼此相邻的开始和结束消息,那么您基本上是在丢弃时间因素,因此可以简单地将您的 beforeInvocation
消息添加到 afterInvocation
方法中如下:
public class TestExecutionListener implements IInvokedMethodListener {
@Override
public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
}
@Override
public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
System.out.println("Testing : " + iInvokedMethod.getTestMethod().getMethodName());
System.out.println("Successfully Tested : " + iInvokedMethod.getTestMethod().getMethodName());
}
}
IMO 这是根据您的规格执行此操作的唯一方法。但是,如果在测试期间必须收集其他信息,那么也许您可以在 TestExecutionListener
中缓冲一些日志,例如:
public class TestExecutionListener implements IInvokedMethodListener {
private Map<Integer, Deque<String>> logsMap = new HashMap<Integer, Deque<String>>();
public void log(IInvokedMethod iInvokedMethod, String log) {
if(!logsMap.containsKey(iInvokedMethod.getId())) {
logsMap.put(iInvokedMethod.getId(), new ArrayDeque<String>());
}
logsMap.get(iInvokedMethod.getId()).add(log);
}
@Override
public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
log(iInvokedMethod, "Testing : " + iInvokedMethod.getTestMethod().getMethodName());
}
@Override
public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
log(iInvokedMethod, "Successfully Tested : " + iInvokedMethod.getTestMethod().getMethodName());
Deque<String> logs = logsMap.get(iInvokedMethod.getId());
while(!logs.isEmpty()) {
System.out.println(logs.poll());
}
}
}
我们一直在使用 testng
和 java
来对我们的代码执行集成测试。我们为测试执行实现了 listener 如下:-
public class TestExecutionListener implements IInvokedMethodListener {
@Override
public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
System.out.println("Testing : " + iInvokedMethod.getTestMethod().getMethodName());
}
@Override
public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
System.out.println("Successfully Tested : " + iInvokedMethod.getTestMethod().getMethodName());
}
}
我们的testng.xml定义为:-
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestSuite" verbose="1" parallel="classes" thread-count="10">
<listeners>
<listener class-name="core.TestExecutionListener"/>
</listeners>
<test name="IntegrationTests">
<classes>
<class name="test.SomeTest1"/>
<class name="test.SomeTest2"/>
<class name="test.SomeTest3"/>
<class name="test.SomeTest4"/>
... There are more than 20 classes
</classes>
</test>
</suite>
当我们执行测试时,我们得到的输出如下:
Testing : SomeTest1Method1 Testing : SomeTest2Method2 Testing : SomeTest4Method5 Successfully Tested : SomeTest2Method2 Successfully Tested : SomeTest4Method5
而我们期望的输出是:-
Testing : SomeTest1Method1 Successfully Tested : SomeTest1Method1 Testing : SomeTest2Method2 Successfully Tested : SomeTest2Method2 Testing : SomeTest4Method5 Successfully Tested : SomeTest4Method5
猜测这是因为 xml 中的 parallel="classes"
属性,因为将其更改为 false
可提供所需的输出。但是很明显,与并行执行相比,更改后的执行会消耗更多的时间。
有没有办法并行运行这些测试但仍然按顺序得到这个输出?
首先,我建议您使用 log4j 或类似的记录器工具,它们优雅、模块化并提供许多灵活的选项。这也为以多种方式设计和处理日志记录机制奠定了基础。
如果您有业务需求,需要您分别跟踪每个线程的日志,您应该考虑将这些日志流式传输到不同的文件。 Log4j 类工具确实支持写入多个文件的需求。你可以google搜索,你会得到很多信息。
当您启用并行时,正如您所注意到的,日志中的 beforeInvocation
和 afterInvocation
之间会有一些时间,并且时间差异因测试而异,因此输出交错。
如果您想要的是彼此相邻的开始和结束消息,那么您基本上是在丢弃时间因素,因此可以简单地将您的 beforeInvocation
消息添加到 afterInvocation
方法中如下:
public class TestExecutionListener implements IInvokedMethodListener {
@Override
public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
}
@Override
public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
System.out.println("Testing : " + iInvokedMethod.getTestMethod().getMethodName());
System.out.println("Successfully Tested : " + iInvokedMethod.getTestMethod().getMethodName());
}
}
IMO 这是根据您的规格执行此操作的唯一方法。但是,如果在测试期间必须收集其他信息,那么也许您可以在 TestExecutionListener
中缓冲一些日志,例如:
public class TestExecutionListener implements IInvokedMethodListener {
private Map<Integer, Deque<String>> logsMap = new HashMap<Integer, Deque<String>>();
public void log(IInvokedMethod iInvokedMethod, String log) {
if(!logsMap.containsKey(iInvokedMethod.getId())) {
logsMap.put(iInvokedMethod.getId(), new ArrayDeque<String>());
}
logsMap.get(iInvokedMethod.getId()).add(log);
}
@Override
public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
log(iInvokedMethod, "Testing : " + iInvokedMethod.getTestMethod().getMethodName());
}
@Override
public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
log(iInvokedMethod, "Successfully Tested : " + iInvokedMethod.getTestMethod().getMethodName());
Deque<String> logs = logsMap.get(iInvokedMethod.getId());
while(!logs.isEmpty()) {
System.out.println(logs.poll());
}
}
}