我们的测试环境有各种依赖中间件的集成测试(CMS平台,底层数据库,Elasticsearch索引)。
它们是自动化的,我们使用Docker管理我们的中间件,因此我们没有不可靠网络的问题。但是,有时我们的数据库崩溃,我们的测试失败。
问题在于,通过一连串的org.hibernate.exception.JDBCConnectionException
消息来检测此故障。这些是通过超时发生的。当发生这种情况时,我们最终会遇到数百个因此异常而失败的测试,每个测试都需要很长时间才能失败。因此,我们的测试需要年龄才能完成。实际上,当我们意识到它们已经完成时,我们通常会手动终止这些构建。
我的问题:在Maven驱动的Java测试环境中,有没有办法指导构建系统注意特定类型的异常并杀死整个过程,如果它们到达(或达到某种阈值)?
我们可以监视我们的容器并以这种方式杀死构建过程,但我希望有一种更清洁的方法来使用maven。
答案 0 :(得分:7)
如果使用TestNG而不是JUnit,还有其他可能将测试定义为依赖于其他测试。
例如,与上面提到的其他人一样,您可以使用一种方法来检查数据库连接,并将所有其他测试声明为依赖于此方法。
@Test
public void serverIsReachable() {}
@Test(dependsOnMethods = { "serverIsReachable" })
public void queryTestOne() {}
这样,如果serverIsReachable
测试失败,将跳过依赖于此测试的所有其他测试,未标记为失败。跳过的方法将在最终报告中报告,这很重要,因为跳过的方法不一定是失败的。 但,因为您的初始测试serverIsReachable
失败,构建应该完全失败。
积极的效果是,你的其他测试不会被执行,这应该非常快地失败。
您还可以使用组扩展此逻辑。让我们假设您之后的某些域逻辑测试使用了数据库查询,您可以使用组声明每个数据库测试,例如
@Test(groups = { "jdbc" })
public void queryTestOne() {}
并使用
将域逻辑测试声明为依赖于这些测试@Test(dependsOnGroups = { "jdbc.* })
public void domainTestOne() {}
因此,TestNG将保证测试的执行顺序。
希望这有助于使您的测试更有条理。有关更多信息,请查看the TestNG dependency documentation。
答案 1 :(得分:4)
我意识到这并不是你要求的,但是可以帮助你加速构建:
JUnit assumptions允许在假设失败时通过测试。您可能会有assumeThat(db.isReachable())
之类的假设,当达到超时时会跳过这些测试。
为了实际加快速度而不是一遍又一遍地重复,你可以把它放在@ClassRule
:
@Before或@BeforeClass方法中的失败假设将与该类的每个@Test方法中的失败假设具有相同的效果。
因为你必须通过另一种方式将你的构建标记为不稳定,但这应该很容易实现。
答案 2 :(得分:2)
我不知道你是否可以快速失败 - 构建本身,甚至想要 - 因为构建的管理方面可能不会完成,但你可以这样做:
在依赖于数据库的所有测试类中 - 或者父类,因为这样的东西是可继承的 - 添加:
@BeforeClass
public void testJdbc() throws Exception {
Executors.newSingleThreadExecutor()
.submit(new Callable() {
public Object call() throws Exception {
// execute the simplest SQL you can, eg. "SELECT 1"
return null;
}
})
.get(100, TimeUnit.MILLISECONDS);
}
如果JDBC简单查询未能在100毫秒内返回,则整个测试类将不会运行,并将显示为构建的“失败”。
让等待时间尽可能小,仍然可靠。
答案 3 :(得分:1)
你可以做的一件事是编写一个新的测试运行器,如果发生这样的错误就会停止。以下是可能的示例:
import org.junit.internal.AssumptionViolatedException;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
public class StopAfterSpecialExceptionRunner extends BlockJUnit4ClassRunner {
private boolean failedWithSpecialException = false;
public StopAfterSpecialExceptionRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (failedWithSpecialException || isIgnored(method)) {
notifier.fireTestIgnored(description);
} else {
runLeaf(methodBlock(method), description, notifier);
}
}
@Override
protected Statement methodBlock(FrameworkMethod method) {
return new FeedbackIfSpecialExceptionOccurs(super.methodBlock(method));
}
private class FeedbackIfSpecialExceptionOccurs extends Statement {
private final Statement next;
public FeedbackIfSpecialExceptionOccurs(Statement next) {
super();
this.next = next;
}
@Override
public void evaluate() throws Throwable {
boolean complete = false;
try {
next.evaluate();
complete = true;
} catch (AssumptionViolatedException e) {
throw e;
} catch (SpecialException e) {
StopAfterSpecialExceptionRunner.this.failedWithSpecialException = true;
throw e;
}
}
}
}
然后使用@RunWith(StopAfterSpecialExceptionRunner.class)
注释您的测试类。
基本上它的作用是它检查某个异常(这里是SpecialException
,我自己编写的异常)如果发生这种情况,它将无法通过测试而失败并跳过所有关注试验。如果您愿意,您当然可以将其限制为使用特定注释进行注释的测试。
使用Rule
可以实现类似的行为,如果是这样,可能会更加清晰。