我正在将测试框架从JUnit迁移到TestNG。该框架用于执行与Selenium的大型端到端集成测试,该测试需要运行几分钟,并包含数十个浏览器页面的数百个步骤。
免责声明:我知道这使单元测试理想主义者非常不舒服,但是大多数面向服务的大型公司都需要进行这种测试,并且使用单元测试工具来管理这些集成测试是目前最广泛的解决方案。这不是我的决定。这是我被要求从事的工作,并且我正在努力做到最好。
无论如何,这些测试非常频繁地失败(惊奇),使它们易于调试非常重要。出于这个原因,我们希望在报告之前检测到测试失败,附加一些有关失败的信息,然后使用这些额外的信息允许JUnit失败。例如,如果没有此信息,则故障可能看起来像:
java.lang.<'SomeObscureException'>: <'Some obscure message'> at <'StackTrace'>
但是随着添加的信息,它将看起来像:
java.lang.AssertionError:
Reproduction Seed: <'Random number used to generate test case'>
Country: <'Country for which test was set to run'>
Language: <'Localized language used by test'>
Step: <'Test step where the exception occurred'>
Exception Message: <'Message explaining probable cause of failure'>
Associated Exception Type: <'SomeObscureException'>
Associated Exception Message: <'Some obscure message'>
Associated Exception StackTrace: <'StackTrace'>
Exception StackTrace: <'StackTrace where we appended this information'>
重要的是要注意,我们在测试实际失败之前添加了此信息。因为我们的报告工具完全基于JUnit抛出的异常,所以可以确保这些异常中包含我们所需的信息。理想情况下,我想在测试失败后但执行拆卸之前,使用报告程序类将这些信息添加到HTML或XML文档中,然后修改我们的报告工具以获取这些额外信息并将其附加到我们的电子邮件报告中。但是,在我们的sprint计划会议上,这一直是一件很难的事,而且我也没有任何时间来分配它(与开发人员测试框架本身相比,为开发人员运行无休止的回归给予了更高的优先权。现代SDET)。我也非常坚信平衡,并且拒绝参与生活的其他部分,以在预定的时间之外完成这项工作。
我们目前正在做的是:
public class SomeTests extends TestBase {
@Test
public void someTest() {
// Test code
}
// More tests
}
public abstract class TestBase {
@Rule
public MyWatcher watcher = new MyWatcher();
// More rules and variables
@Before
public final void setup() {
// Read config, generate test data, create Selenium WebDriver, etc.
// Send references to all test objects to MyWatcher
}
}
public class MyWatcher extends TestWatcher {
// Test object references
@Override
public void failed(Throwable throwable, Description description) {
StringBuilder sb = new StringBuilder();
// Append custom test information to sb.
String exceptionSummary = sb.toString();
Assert.fail(exceptionSummary);
}
@Override
public void finished(Description description) {
// Shut down Selenium WebDriver, kill proxy server, etc.
}
// Miscellaneous teardown and logging methods
}
SomeTests
继承自TestBase
类。 TestBase
通过TestWatcher
注释(@Rule
)实例化我们自己的MyWatcher
实例。 TestBase
类中运行。 MyWatcher
。 someTest()
方法。 someTest
在某些时候失败。 failed()
中重写的MyWatcher
方法。 failed()
方法使用TestBase
传递的引用将自定义测试信息附加到新消息中。 failed()
方法使用自定义消息调用JUnit的Assert.fail()
方法。 java.lang.Assertion
错误。这是实际记录在测试结果中的例外。 finished()
方法。 finished()
方法执行测试拆卸。 MyWatcher
添加任何额外信息,调试原始异常将变得更加轻松。 我现在想使用TestNG实现类似的机制。我首先尝试在@Listener批注中向我们的IInvokedMethodListener
类添加TestBase
,以替换我们在JUnit中使用的TestWatcher
。不幸的是,在每次@BeforeMethod
和@AfterMethod
调用以及实际测试之后,都将调用此侦听器中的方法。当我从IInvokedMethodListener
内部调用Assert.fail时,这造成了相当大的混乱,因此我选择取消这种方法,并将代码直接插入到@AfterMethod
类的TestBase
调用中。
不幸的是,TestNG似乎无法处理我们在JUnit中使用的“两次失败”方法。当我在已经失败的测试的@AfterMethod
中调用Assert.fail时,它被报告为其他失败。似乎我们将不得不想出另一种方式来执行此操作,直到获得获得编写包含调试所需信息的适当测试报告程序的授权为止。
同时,我们仍然需要处理TestNG引发的异常,以便调试信息将出现在我们的电子邮件报告中。我这样做的一个想法是将每个测试包装在try / catch块中。如果测试失败(抛出异常),那么我们可以捕获该异常,将其打扮为摘要异常,并在该异常的消息中添加调试信息,然后使用新的摘要异常调用Assert.fail。这样,TestNG只会看到一个异常,并且应该只报告一个失败。但是,这感觉就像是在垃圾之上的垃圾,我不禁感到有更好的方法可以做到这一点。
有谁知道一种更好的方法来修改TestNG报告的内容?我可以使用ITestContext
或ITestResult
用某种技巧来替换原始异常吗?我可以潜入某个地方并从某个列表中删除原始故障,还是在进入@AfterMethod
函数之前停止TestNG的内部报告已经为时已晚?
对于这种测试或异常处理,您还有其他建议吗?我没有很多知识渊博的同事来帮助您解决这些问题,因此我几乎只是想侧翼而已。
参考方案
实现IInvokedMethodListener
:
public class InvokedMethodListener implements IInvokedMethodListener {
@Override
public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
}
@Override
public void afterInvocation(IInvokedMethod method, ITestResult result) {
if (method.isTestMethod() && ITestResult.FAILURE == result.getStatus()) {
Throwable throwable = result.getThrowable();
String originalMessage = throwable.getMessage();
String newMessage = originalMessage + "\nReproduction Seed: ...\nCountry: ...";
try {
FieldUtils.writeField(throwable, "detailMessage", newMessage, true);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
在测试中注册:
@Listeners(InvokedMethodListener.class)
public class YourTest {
@Test
public void test() {
Assert.fail("some message");
}
}
或testng.xml
中。
如果执行它,应该得到:
java.lang.AssertionError: some message
Reproduction Seed: ...
Country: ...
Java-如何将此字符串转换为日期? - java我从服务器收到此消息,我不明白T和Z的含义,2012-08-24T09:59:59Z将此字符串转换为Date对象的正确SimpleDateFormat模式是什么? java大神给出的解决方案 这是ISO 8601标准。您可以使用SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM…
Java-NoSuchMethodError没有被异常捕获 - java我的印象是,Exception非常适合捕获所有可能的异常,因为每个异常都具有Exception作为基类。然后,在开发Android应用程序时,我使用了以下方法,该方法在某些自定义ROM中已被删除。boolean result = false; try{ result = Settings.canDrawOverlays(context); } catch(E…
Java:捕获异常-未选中与已选中 - java我有一些代码将字符串(用户输入)拆分为一个数组,并将该数组的元素作为参数传递给方法。如果数组没有足够的元素,则会自动引发ArrayIndexOutOfBoundsException。但是,这是一个未经检查的异常,并且由于这是用户输入错误的问题,因此我可以事先检查此条件并抛出一个检查的异常。因此,我有几个问题:处理未检查的异常还是抛出检查的异常会更好吗?如果确…
Java:正则表达式模式匹配器是否有大小限制? - java我的模式类似于OR:“word1 | word2 | word3”我大约有800个字。可能有问题吗? 参考方案 您仅受记忆和理智的限制。 :)
Java:线程池如何将线程映射到可运行对象 - java试图绕过Java并发问题,并且很难理解线程池,线程以及它们正在执行的可运行“任务”之间的关系。如果我创建一个有10个线程的线程池,那么我是否必须将相同的任务传递给池中的每个线程,或者池化的线程实际上只是与任务无关的“工人无人机”可用于执行任何任务?无论哪种方式,Executor / ExecutorService如何将正确的任务分配给正确的线程? 参考方案 …