【问题标题】:Is there a way to make integration tests fail quickly when middleware fails?当中间件失败时,有没有办法让集成测试快速失败?
【发布时间】:2015-03-17 16:43:50
【问题描述】:

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

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

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

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

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

【问题讨论】:

标签: java maven integration-testing


【解决方案1】:

如果您使用 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

【讨论】:

    【解决方案2】:

    我意识到这并不是您所要求的,但仍然可以帮助加快构建速度:

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

    为了真正加快速度并且不一遍又一遍地重复此操作,您可以将其放入@ClassRule

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

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

    【讨论】:

      【解决方案3】:

      我不知道您是否可以对构建本身进行快速失败,或者甚至想要 - 因为构建的管理方面可能无法完成,但您可以这样做:

      在所有依赖于数据库的测试类或父类中,因为这样的东西是可继承的 - 添加这个:

      @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 毫秒内返回,则整个测试类将不会运行,并将显示为构建“失败”。

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

      【讨论】:

        【解决方案4】:

        您可以做的一件事是编写一个新的 Test Runner,如果发生此类错误,它将停止。这是一个可能看起来像的示例:

        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 可以实现类似的行为,如果是这样,那可能会更干净。

        【讨论】:

          猜你喜欢
          • 2012-09-01
          • 2022-11-21
          • 2010-12-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-28
          • 2020-02-01
          • 1970-01-01
          相关资源
          最近更新 更多