web-dev-qa-db-ja.com

@SpringBootTest + @BeforeAll

私はデータベースとrabbitmqの使用法を備えた小さな春のブートアプリを持っています。そこで、統合テスト(H2 + Apache qpid)でテストしたいと思います。

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = TestSpringConfig.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)

私のアプリはデータベースとmqImが@BeforeAllを使用して起動することを期待しているため:

@BeforeAll
public void before() {
    startMessageBroker();
    startDatabase();
}

問題は、@ BeforeAllで定義されたdatabase/mqの前にWebアプリが起動することです。

org.springframework.test.context.junit.jupiter.SpringExtension:

public class SpringExtension implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor,
        BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback,
        ParameterResolver {
// ...
    @Override
    public void beforeAll(ExtensionContext context) throws Exception {
        getTestContextManager(context).beforeTestClass();
    }
// ...
    @Override
    public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
        getTestContextManager(context).prepareTestInstance(testInstance);
    }
// ...

WebアプリはpostProcessTestInstanceフェーズで起動し、@ BeforeAllメソッドはbeforeAllで起動します。

org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor:

private void execute(TestDescriptor testDescriptor, C parentContext, ExecutionTracker tracker) {
    Node<C> node = asNode(testDescriptor);
    tracker.markExecuted(testDescriptor);

    C preparedContext;
    try {
        preparedContext = node.prepare(parentContext); // 1 <<<
        SkipResult skipResult = node.shouldBeSkipped(preparedContext);
        if (skipResult.isSkipped()) {
            this.listener.executionSkipped(testDescriptor, skipResult.getReason().orElse("<unknown>"));
            return;
        }
    }
    catch (Throwable throwable) {
        rethrowIfBlacklisted(throwable);
        // We call executionStarted first to comply with the contract of EngineExecutionListener
        this.listener.executionStarted(testDescriptor);
        this.listener.executionFinished(testDescriptor, TestExecutionResult.failed(throwable));
        return;
    }

    this.listener.executionStarted(testDescriptor);

    TestExecutionResult result = singleTestExecutor.executeSafely(() -> {
        C context = preparedContext;
        try {
            context = node.before(context); // 2 <<<

            C contextForDynamicChildren = context;
            context = node.execute(context, dynamicTestDescriptor -> {
                this.listener.dynamicTestRegistered(dynamicTestDescriptor);
                execute(dynamicTestDescriptor, contextForDynamicChildren, tracker);
            });

            C contextForStaticChildren = context;
            // @formatter:off
            testDescriptor.getChildren().stream()
                    .filter(child -> !tracker.wasAlreadyExecuted(child))
                    .forEach(child -> execute(child, contextForStaticChildren, tracker));
            // @formatter:on
        }
        finally {
            node.after(context);
        }
    });

    this.listener.executionFinished(testDescriptor, result);
}

ポイント1と2を参照してください。「prepare」と「before」の実行があります。

それがjunit、SpringExtension、またはImの問題であるかどうかはわかりません。何かアドバイス?

junit-jupiter:5.0.1

春のテスト:5.0.0.RELEASE

spring-boot-test:1.5.8.RELEASE

7
Alex

チェックアウト https://www.testcontainers.org/ JUnitとの統合を提供し、JUnitテストの一部としてRabbitMQとDockerコンテナー内のデータベースを起動します。これにより、本番環境で使用するのと同じバージョンのデータベースとメッセージキューを使用するため、統合テストが非常に現実的になります。

1
ams

テストクラスでDBとメッセージブローカーを起動する理由はありますか?これは設計上間違っているように思われます。これらは両方ともインフラストラクチャの一部であるため、アプリケーションコンテキストとともに開始する必要があります。

インフラストラクチャをセットアップするのはテストの責任ではありません!

私見、物事を行うためのより良い方法は次のとおりです。

  • MavenのtestスコープでH2依存関係を使用し、アプリケーションコンテキストの開始時にH2を開始するようにスターターを構成します
  • アプリケーションの起動時にApacheqpid(できれば組み込み)を起動します
  • @Beforeでは、テストケースを実行する前に必ずクリーンアップしてください
0
Danylo Zatorsky

これは仕様によるものだと思います。 Beanポストプロセッサ/コンテキスト初期化子を追加して、DB/rabbitMQを初期化/開始してみてください。

0
Alexander.Furer