IRetryAnalyzer 为定义为 SoftAssert 的测试用例生成不正确的结果
IRetryAnalyzer produces incorrect results for test cases defined as SoftAssert
我使用 IRetryAnalyzer 为失败的测试用例实现了重试逻辑,并且有 2 种类型的断言 - 我的测试用例中定义了 Assert 和 SoftAssert。 IRetryAnayzer 对于普通断言工作正常,但在 SoftAssert 的情况下无法按预期工作。以下是有关所面临问题的场景详细信息:
- 如果定义为SoftAssert的测试用例第一次尝试失败,下次尝试通过,即使测试用例通过,它也会继续重试直到最大重试次数。在这种情况下,如果定义为正常断言(非软断言)的下一个测试用例通过,即使它通过了它也会被标记为失败,并且将重试定义的最大重试次数。
- 如果所有测试用例都定义为正常断言,它会按预期工作,即如果第一次尝试失败并在下一次尝试中通过,它会继续进行并且不会陷入重试循环。
- 如果定义为 SoftAssert 的测试用例在第一次尝试中通过,它不会重试并继续下一个测试用例,即它按预期工作。
我需要保留一些测试用例作为 softAssert,因为我必须继续测试 运行。例如:
@Test(retryAnalyzer = RetryAnalyzer.class, groups = { "group1" }, priority=1)
public void TestSection1(){
Class1.verifyingAppLaunch(); //Defined as Assert
Class1.Test1(); //Defined as softAssert
Class1.Test2(); //Defined as softAssert
Class1.Test3(); //Defined as softAssert
Class1.Test4(); //Defined as softAssert
Class1.Test5(); //Defined as softAssert
softAssert.assertAll();
}
下面是 IRetryAnalyer 和 ListenerAdapter 实现的示例。实施 ListenerAdapter 是为了删除重复的测试用例执行,这些测试用例执行被标记为已跳过,作为重试实施的一部分。在下面的示例代码中,如果 samplecondition1 在第一次尝试中失败,它会重试定义的最大重试次数,即使它在第二次尝试中通过,也会将 samplecondition2 标记为失败,即使它通过:
MyTestListenerAdapter.class
import java.util.Iterator;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
public class MyTestListenerAdapter extends TestListenerAdapter {
@Override
public void onFinish(ITestContext context) {
Iterator<ITestResult> skippedTestCases = context.getSkippedTests().getAllResults().iterator();
while (skippedTestCases.hasNext()) {
ITestResult skippedTestCase = skippedTestCases.next();
ITestNGMethod method = skippedTestCase.getMethod();
if (context.getSkippedTests().getResults(method).size() > 0) {
System.out.println("Removing:" + skippedTestCase.getTestClass().toString());
skippedTestCases.remove();
}
}
}
}
TestRetryAnalyzer.class
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
public class TestRetryAnalyzer implements IRetryAnalyzer {
int counter = 1;
int retryMaxLimit = 3;
public boolean retry(ITestResult result) {
if (counter < retryMaxLimit) {
counter++;
return true;
}
return false;
}
}
TestRetryTestCases.class
import org.testng.Assert;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MyTestListenerAdapter.class)
public class TestRetryTestCases {
SoftAssert softAssert = new SoftAssert();
@Test(retryAnalyzer = TestRetryAnalyzer.class)
public void firstTestMethod() {
System.out.println("First test method");
if (samplecondition1 == true)
softAssert.assertTrue(true);
else
softAssert.assertTrue(false);
softAssert.assertAll();
}
@Test(retryAnalyzer = TestRetryAnalyzer.class)
public void secondTestMethod() {
System.out.println("Second test method");
if (samplecondition2 == true)
Assert.assertTrue(true);
else
Assert.assertTrue(false);
}
}
我不确定您使用的是哪个版本的 TestNG,但我似乎无法使用 TestNG 7.0.0
(截至今天的最新发布版本)重现此问题。
您的代码中还有其他问题。您将 SoftAssert
作为全局变量。 SoftAssert
通过它的实现,记住所有的失败。因此,对于每次重试,它都会继续坚持从第一次尝试到现在的所有失败。这意味着涉及 RetryAnalyser
并使用 SoftAssert
的 @Test
方法可能会导致该测试方法永远不会通过。
使用 SoftAssert
时,您应该始终在 @Test
方法中声明和使用 SoftAssert
对象,以便它被实例化(并因此在每次重试时重置)。
这是您分享的同一个示例(我稍微调整了一下),它证明这在 7.0.0
中工作正常
正如您从输出中看到的那样,只有 firstTestMethod
(其中有 SoftAssert
正在重试)和 secondTestMethod
(具有硬断言并且没有失败) 未重试。
测试class(我只改了这个,其他都是借鉴你原来的post)
import org.testng.Assert;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import org.testng.asserts.SoftAssert;
@Listeners(MyTestListenerAdapter.class)
public class TestRetryTestCases {
int softAssertCounter = 0;
int hardAssertCounter = 0;
@Test(retryAnalyzer = TestRetryAnalyzer.class)
public void firstTestMethod() {
SoftAssert softAssert = new SoftAssert();
System.out.println("First test method");
if (softAssertCounter++ > 2) {
softAssert.assertTrue(true);
} else {
softAssert.assertTrue(false);
}
softAssert.assertAll();
}
@Test(retryAnalyzer = TestRetryAnalyzer.class)
public void secondTestMethod() {
System.out.println("Second test method");
if (hardAssertCounter++ < 2) {
Assert.assertTrue(true);
} else {
Assert.assertTrue(false);
}
}
}
** 使用一些额外的日志重试分析器 **
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
public class TestRetryAnalyzer implements IRetryAnalyzer {
int counter = 1;
int retryMaxLimit = 3;
public boolean retry(ITestResult result) {
if (counter < retryMaxLimit) {
counter++;
System.err.println("Retrying the test method " + result.getMethod().getMethodName());
return true;
}
return false;
}
}
控制台输出
First test method
Retrying the test method firstTestMethod
Retrying the test method firstTestMethod
Test ignored.
First test method
Test ignored.
First test method
java.lang.AssertionError: The following asserts failed:
did not expect to find [true] but found [false]
at org.testng.asserts.SoftAssert.assertAll(SoftAssert.java:47)
at org.testng.asserts.SoftAssert.assertAll(SoftAssert.java:31)
at com.rationaleemotions.Whosebug.qn58072880.TestRetryTestCases.firstTestMethod(TestRetryTestCases.java:22)
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.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:133)
at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:584)
at org.testng.internal.TestInvoker.retryFailed(TestInvoker.java:204)
at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:58)
at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:804)
at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:145)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at org.testng.TestRunner.privateRun(TestRunner.java:770)
at org.testng.TestRunner.run(TestRunner.java:591)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:402)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:396)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:355)
at org.testng.SuiteRunner.run(SuiteRunner.java:304)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1180)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1102)
at org.testng.TestNG.runSuites(TestNG.java:1032)
at org.testng.TestNG.run(TestNG.java:1000)
at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:73)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)
Second test method
Removing:[TestClass name=class com.rationaleemotions.Whosebug.qn58072880.TestRetryTestCases]
Removing:[TestClass name=class com.rationaleemotions.Whosebug.qn58072880.TestRetryTestCases]
===============================================
Default Suite
Total tests run: 2, Passes: 1, Failures: 1, Skips: 0
===============================================
Process finished with exit code 0
我使用 IRetryAnalyzer 为失败的测试用例实现了重试逻辑,并且有 2 种类型的断言 - 我的测试用例中定义了 Assert 和 SoftAssert。 IRetryAnayzer 对于普通断言工作正常,但在 SoftAssert 的情况下无法按预期工作。以下是有关所面临问题的场景详细信息:
- 如果定义为SoftAssert的测试用例第一次尝试失败,下次尝试通过,即使测试用例通过,它也会继续重试直到最大重试次数。在这种情况下,如果定义为正常断言(非软断言)的下一个测试用例通过,即使它通过了它也会被标记为失败,并且将重试定义的最大重试次数。
- 如果所有测试用例都定义为正常断言,它会按预期工作,即如果第一次尝试失败并在下一次尝试中通过,它会继续进行并且不会陷入重试循环。
- 如果定义为 SoftAssert 的测试用例在第一次尝试中通过,它不会重试并继续下一个测试用例,即它按预期工作。
我需要保留一些测试用例作为 softAssert,因为我必须继续测试 运行。例如:
@Test(retryAnalyzer = RetryAnalyzer.class, groups = { "group1" }, priority=1)
public void TestSection1(){
Class1.verifyingAppLaunch(); //Defined as Assert
Class1.Test1(); //Defined as softAssert
Class1.Test2(); //Defined as softAssert
Class1.Test3(); //Defined as softAssert
Class1.Test4(); //Defined as softAssert
Class1.Test5(); //Defined as softAssert
softAssert.assertAll();
}
下面是 IRetryAnalyer 和 ListenerAdapter 实现的示例。实施 ListenerAdapter 是为了删除重复的测试用例执行,这些测试用例执行被标记为已跳过,作为重试实施的一部分。在下面的示例代码中,如果 samplecondition1 在第一次尝试中失败,它会重试定义的最大重试次数,即使它在第二次尝试中通过,也会将 samplecondition2 标记为失败,即使它通过:
MyTestListenerAdapter.class
import java.util.Iterator;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
public class MyTestListenerAdapter extends TestListenerAdapter {
@Override
public void onFinish(ITestContext context) {
Iterator<ITestResult> skippedTestCases = context.getSkippedTests().getAllResults().iterator();
while (skippedTestCases.hasNext()) {
ITestResult skippedTestCase = skippedTestCases.next();
ITestNGMethod method = skippedTestCase.getMethod();
if (context.getSkippedTests().getResults(method).size() > 0) {
System.out.println("Removing:" + skippedTestCase.getTestClass().toString());
skippedTestCases.remove();
}
}
}
}
TestRetryAnalyzer.class
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
public class TestRetryAnalyzer implements IRetryAnalyzer {
int counter = 1;
int retryMaxLimit = 3;
public boolean retry(ITestResult result) {
if (counter < retryMaxLimit) {
counter++;
return true;
}
return false;
}
}
TestRetryTestCases.class
import org.testng.Assert;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MyTestListenerAdapter.class)
public class TestRetryTestCases {
SoftAssert softAssert = new SoftAssert();
@Test(retryAnalyzer = TestRetryAnalyzer.class)
public void firstTestMethod() {
System.out.println("First test method");
if (samplecondition1 == true)
softAssert.assertTrue(true);
else
softAssert.assertTrue(false);
softAssert.assertAll();
}
@Test(retryAnalyzer = TestRetryAnalyzer.class)
public void secondTestMethod() {
System.out.println("Second test method");
if (samplecondition2 == true)
Assert.assertTrue(true);
else
Assert.assertTrue(false);
}
}
我不确定您使用的是哪个版本的 TestNG,但我似乎无法使用 TestNG 7.0.0
(截至今天的最新发布版本)重现此问题。
您的代码中还有其他问题。您将 SoftAssert
作为全局变量。 SoftAssert
通过它的实现,记住所有的失败。因此,对于每次重试,它都会继续坚持从第一次尝试到现在的所有失败。这意味着涉及 RetryAnalyser
并使用 SoftAssert
的 @Test
方法可能会导致该测试方法永远不会通过。
使用 SoftAssert
时,您应该始终在 @Test
方法中声明和使用 SoftAssert
对象,以便它被实例化(并因此在每次重试时重置)。
这是您分享的同一个示例(我稍微调整了一下),它证明这在 7.0.0
正如您从输出中看到的那样,只有 firstTestMethod
(其中有 SoftAssert
正在重试)和 secondTestMethod
(具有硬断言并且没有失败) 未重试。
测试class(我只改了这个,其他都是借鉴你原来的post)
import org.testng.Assert;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import org.testng.asserts.SoftAssert;
@Listeners(MyTestListenerAdapter.class)
public class TestRetryTestCases {
int softAssertCounter = 0;
int hardAssertCounter = 0;
@Test(retryAnalyzer = TestRetryAnalyzer.class)
public void firstTestMethod() {
SoftAssert softAssert = new SoftAssert();
System.out.println("First test method");
if (softAssertCounter++ > 2) {
softAssert.assertTrue(true);
} else {
softAssert.assertTrue(false);
}
softAssert.assertAll();
}
@Test(retryAnalyzer = TestRetryAnalyzer.class)
public void secondTestMethod() {
System.out.println("Second test method");
if (hardAssertCounter++ < 2) {
Assert.assertTrue(true);
} else {
Assert.assertTrue(false);
}
}
}
** 使用一些额外的日志重试分析器 **
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
public class TestRetryAnalyzer implements IRetryAnalyzer {
int counter = 1;
int retryMaxLimit = 3;
public boolean retry(ITestResult result) {
if (counter < retryMaxLimit) {
counter++;
System.err.println("Retrying the test method " + result.getMethod().getMethodName());
return true;
}
return false;
}
}
控制台输出
First test method
Retrying the test method firstTestMethod
Retrying the test method firstTestMethod
Test ignored.
First test method
Test ignored.
First test method
java.lang.AssertionError: The following asserts failed:
did not expect to find [true] but found [false]
at org.testng.asserts.SoftAssert.assertAll(SoftAssert.java:47)
at org.testng.asserts.SoftAssert.assertAll(SoftAssert.java:31)
at com.rationaleemotions.Whosebug.qn58072880.TestRetryTestCases.firstTestMethod(TestRetryTestCases.java:22)
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.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:133)
at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:584)
at org.testng.internal.TestInvoker.retryFailed(TestInvoker.java:204)
at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:58)
at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:804)
at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:145)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at org.testng.TestRunner.privateRun(TestRunner.java:770)
at org.testng.TestRunner.run(TestRunner.java:591)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:402)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:396)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:355)
at org.testng.SuiteRunner.run(SuiteRunner.java:304)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1180)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1102)
at org.testng.TestNG.runSuites(TestNG.java:1032)
at org.testng.TestNG.run(TestNG.java:1000)
at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:73)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)
Second test method
Removing:[TestClass name=class com.rationaleemotions.Whosebug.qn58072880.TestRetryTestCases]
Removing:[TestClass name=class com.rationaleemotions.Whosebug.qn58072880.TestRetryTestCases]
===============================================
Default Suite
Total tests run: 2, Passes: 1, Failures: 1, Skips: 0
===============================================
Process finished with exit code 0