有没有一种方法可以在中间件失败时使集成测试快速失败



我们的测试环境有各种依赖中间件的集成测试(CMS平台、底层数据库、Elasticsearch索引)。

它们是自动化的,我们使用Docker管理中间件,因此我们不会遇到不可靠网络的问题。然而,有时我们的数据库会崩溃,我们的测试也会失败。

问题是通过一连串的org.hibernate.exception.JDBCConnectionException消息来检测此故障。这些都是通过超时来实现的。当这种情况发生时,除了这个例外,我们最终会有数百个测试失败,每个测试都需要几秒钟才能失败。因此,我们的测试需要年龄才能完成。事实上,当我们意识到这些构建已经完成时,我们通常只是手动终止它们。

我的问题是:在Maven驱动的Java测试环境中,是否有一种方法可以指导构建系统注意特定类型的异常,并在它们到达(或达到某种阈值)时终止整个过程?

我们可以监视我们的容器并以这种方式终止构建过程,但我希望有一种更干净的方法来使用maven。

如果您使用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将保证测试的执行顺序。

希望这能帮助你的测试更有条理。有关更多信息,请查看TestNG依赖文档。

我意识到这并不完全是你所要求的,但可以帮助加快构建:

JUnit假设允许在假设失败时让测试通过。您可以有一个类似assumeThat(db.isReachable())的假设,当达到超时时会跳过这些测试。

为了真正加快速度,并且不重复这个过程,你可以把它放在@ClassRule:中

@Before或@BeforeClass方法中的失败假设与该类的每个@Test方法中的错误假设具有相同的效果。

当然,您必须通过另一种方式将构建标记为不稳定,但这应该很容易实现。

我不知道你是否可以快速失败构建本身,甚至想失败——因为构建的管理方面可能还没有完成,但你可以这样做:

在所有依赖于数据库或父类的测试类中,因为类似的东西是可继承的,请添加以下内容:

@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简单查询未能在100ms内返回,那么整个测试类将不会运行,并且将显示为构建的"失败"。

尽可能缩短等待时间,并且仍然可靠。

您可以做的一件事是编写一个新的测试运行程序,如果发生此类错误,该程序将停止。下面是一个可能的例子:

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也有可能实现类似的行为,如果是这样,可能会干净得多。

最新更新