有没有办法让中间件失败时集成测试快速失败?

时间:2015-03-17 16:43:51

标签: java maven integration-testing

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

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

问题在于,通过一连串的org.hibernate.exception.JDBCConnectionException消息来检测此故障。这些是通过超时发生的。当发生这种情况时,我们最终会遇到数百个因此异常而失败的测试,每个测试都需要很长时间才能失败。因此,我们的测试需要年龄才能完成。实际上,当我们意识到它们已经完成时,我们通常会手动终止这些构建。

我的问题:在Maven驱动的Java测试环境中,有没有办法指导构建系统注意特定类型的异常并杀死整个过程,如果它们到达(或达到某种阈值)?

我们可以监视我们的容器并以这种方式杀死构建过程,但我希望有一种更清洁的方法来使用maven。

4 个答案:

答案 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可以实现类似的行为,如果是这样,可能会更加清晰。