Helidon 2.1.0 is released

原文はこちら。
The original articles were listed below.
https://medium.com/helidon/helidon-2-1-0-is-released-95c89d02367
by Dmitry Kornilov (Eclipse EE4J PMC member, Jakarta EE contributor, JCP star spec lead, Helidon Project lead, Oracle).
https://medium.com/helidon/testing-helidon-9df2ea14e22
by Tomáš Langer (Oracle).

Helidon 2.1.0 がリリースされました。不具合の修正だけでなく、MicroProfile 3.3のサポート、新たなFaultTolerance実装や@HelidonTestというアノテーションの追加がなされています。

MicroProfile 3.3
https://projects.eclipse.org/projects/technology.microprofile/releases/microprofile-3.3
Maven Central
https://search.maven.org/artifact/io.helidon/helidon-bom/2.1.0/pom
Release Note
https://github.com/oracle/helidon/releases/tag/2.1.0

MicroProfile 3.3

MicroProfile のサポートは Project Helidon の主要な優先事項の一つで、Helidon 2.1.0 で MicroProfile 3.3 をフルサポートしました。MicroProfile 3.3 には以下の仕様のアップデートが含まれます。いずれもマイナーリリースであり、後方互換性を壊すような変更は入っていません。

  • Config 1.4
  • Fault Tolerance 2.1
  • Health 2.2
  • Metrics 2.3
  • Rest Client 1.4

New Fault Tolerance implementation

このリリースで Helidon SE にも Fault Tolerance 機能が追加されました。これまでの Helidon MP のFault Tolerance実装で使われていた Hystrix を置き換え、そのFault Tolerance実装をHelidon SEユーザーにも提供します。これは Hystrix の開発が昨今活発でないことをうけたものです。

詳細は以下をご覧ください。

Fault Tolerance in Helidon SE
https://helidon.io/docs/v2/#/se/faulttolerance/01_faulttolerance

Simplified testing of Microprofile applications

もう一つの興味深い機能として、新たに @HelidonTest というアノテーションが追加されました。これはJUnit 5環境でのMicroProfileアプリケーションのテストをサポートするもので、CDIやHelidon MicroProfileサーバーのライフサイクル管理が可能になるため、アプリケーションのテストに集中できます。

この機能を利用するには、以下の依存関係をテストモジュールに追加します。

<dependency>
    <groupId>io.helidon.microprofile.tests</groupId>
    <artifactId>helidon-microprofile-tests-junit5</artifactId>
    <scope>test</scope>
</dependency>

Quick Startアプリケーションのような簡単なアプリケーションでやってみましょう。

MP — Helidon MP Quickstart
https://helidon.io/docs/v2/#/mp/guides/02_quickstart

このアプリケーションは /greet というエンドポイントを提供しているので、このエンドポイントが利用可能であること、期待される値を返すことを確認しましょう。

空のテストメソッドを持つテストクラスを作成し、 @HelidonTest で注釈をつけます。

import io.helidon.microprofile.tests.junit5.HelidonTest;

import org.junit.jupiter.api.Test;

@HelidonTest
class GreetTest {
    @Test
    void testDefaultGreeting() {
    } 
}

テストの出力からもわかる通り、@HelidonTest アノテーションにより、テスト拡張機能 (extension) がHelidon MicroProfileサーバーを始動します。そのため、テスト中にサーバーのライフサイクルを管理する必要はありません。デフォルトではアプリケーションは8080/tcpを開けますが、テスト時はランダムなポート番号を利用していることもわかります。コンテナが初期化された後にテストクラスをインスタンス化し、最後のテストが実行した後にシャットダウンします。

INFO io.helidon.microprofile.server.ServerCdiExtension: Server started on http://localhost:56293 (and all other host addresses) in 1893 milliseconds (since JVM startup).

このテストは、サーバーを起動して結果を検証する場合に有用なだけですが、テストをサポートするために、現在実行中のサーバー用に設定された WebTarget を注入できます(テストメソッドのパラメータにもなりえます)。WebTargetを使用してエンドポイントを呼び出し、結果を検証してみましょう。

import static org.junit.jupiter.api.Assertions.assertEquals;@HelidonTest
class GreetTest {
    @Inject
    WebTarget webTarget;

    @Test
    void testDefaultGreeting() {
        JsonObject jsonObject = webTarget.path("/greet")
                .request()
                .get(JsonObject.class);

        String expected = "Hello World!";
        String actual = jsonObject.getString("message");
        assertEquals(expected, actual, "Message in JSON");
    }
}

テスト拡張機能

テスト拡張機能はテスト実行をより細かく管理できるよう、いくつかのツールをサポートしています。

以下のように定義することで、メソッドごとにコンテナをリセットできます。

@HelidonTest(resetPerTest = true)

これは実行の間に構成やbeanを変更したい場合に有用です。このような場合、テストごとに異なるインスタンスが必要になるので、フィールドへのインジェクションはできません。

以下のように指定すれば、新たな構成をクラスレベル、もしくはメソッドレベルで)定義することもできます。1個の構成(Key-Value)を追加することもできますし、

@AddConfig(key = "app.greeting", value = "Unite")

クラスパスから構成のソース全体を追加することもできます。

@Configuration(configSources = "test-config.properties")

この2つのアプローチを組み合わせて、同じエンドポイントを異なる構成で実行してみましょう。

@HelidonTest(resetPerTest = true)
class GreetTest {
    @Test
    void testDefaultGreeting(WebTarget webTarget) {
        validate(webTarget, "/greet", "Hello World!");
    }

    @Test
    @AddConfig(key = "app.greeting", value = "Unite")
    void testConfiguredGreeting(WebTarget webTarget) {
        validate(webTarget, "/greet", "Unite World!");
    }

    private void validate(WebTarget webTarget,
                          String path,
                          String expected) {

        JsonObject jsonObject = webTarget.path(path)
                .request()
                .get(JsonObject.class);

        String actual = jsonObject.getString("message");
        assertEquals(expected, actual, "Message in JSON");
    }
}

テスト目的のためだけにbeanを使う必要があり、各テストで異なるbeanを使う必要があると仮定しましょう。これはCDI discoveryでは実現できませんでした。META-INF/beans.xml をクラスパスに配置した場合、すべてのbeanが追加されてしまうためです。

この目的のため、以下のアノテーションが用意されています。

@AddBean(TestBean.class)

このアノテーションでbeanをコンテナに追加します。デフォルトでは ApplicationScoped スコープが設定されます。beanクラスに別のスコープで注釈をつけるか、以下の注釈を使ってスコープを変更できます。

@AddBean(value = TestBean.class, scope = Dependent.class)

resetPerTestモードで実行する場合、このアノテーションをメソッドに配置することもできます。

時として、カスタムbeanでは十分ではなく、CDIをテスト専用のExtensionで拡張しなければならない場合があります。これもまた同様で、これを標準的な方法で行う場合、すべてのテストクラスが使うMETA-INF/servicesのレコードを作成する必要があります。

この目的のため、以下のアノテーションが用意されています。

@AddExtension(TestExtension.class)

拡張をコンテナに追加して、通常のCDI Portable Extensionとしてその振る舞いを変更できます。

最後に、discoveryを無効にしてカスタム拡張やbeanのみを追加したい場合があります。これは最も自由度の高いシナリオですが、この目的のために以下のアノテーションが用意されています。

@DisableDiscovery

このアノテーションは通常、AddBeansAddExtensionと一緒に使用します。テストの標準出力でご覧の通り、HelidonはMicroProfile機能を完全に有効にした状態で開始しますが、基本的な機能だけを有効にしたい場合に利用できます。

次のテストは、必要な拡張機能とクラスだけを使って、設定値を注入するbeanを実行しています。

import javax.inject.Inject;

import io.helidon.microprofile.config.ConfigCdiExtension;
import io.helidon.microprofile.tests.junit5.AddBean;
import io.helidon.microprofile.tests.junit5.AddConfig;
import io.helidon.microprofile.tests.junit5.AddExtension;
import io.helidon.microprofile.tests.junit5.DisableDiscovery;
import io.helidon.microprofile.tests.junit5.HelidonTest;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

@HelidonTest
@DisableDiscovery
@AddExtension(ConfigCdiExtension.class)
@AddBean(GreetTest.ConfiguredBean.class)
@AddConfig(key = "test.message", value = "Hello Blog!")
class GreetTest {
    @Inject
    ConfiguredBean bean;

    @Test
    void testBean() {
        assertEquals("Hello Blog!", bean.message());
    }

    public static class ConfiguredBean {
        @Inject
        @ConfigProperty(name = "test.message")
        private String message;

        String message() {
            return message;
        }
    }
}

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中