JUnitのNestedなMethodSourceの注意点
JUnit5での @MethodSource
のおさらい
JUnit5にはパラメタライズドテスト用の @ParameterizedTest
があり、様々な方法でパラメーターを与えられます。
その中でもパラメーターにある程度柔軟性が欲しい場合によく使うのが @MethodSource
で、テストメソッドのパラメーターを生成できます。
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; public class MyParameterizedTest { @ParameterizedTest @MethodSource void test(String parameter) throws Exception { assertEquals(4, parameter.length()); } static Stream<String> test() { return Stream.of( "hoge", "fuga", "piyo" ); } }
@MethodSource
のvalue
要素は引数のファクトリメソッド名を指定しますが、テストメソッド名と同じであれば省略可能です。
ファクトリメソッドは引数無しのstatic
メソッドで、テストメソッドは引数ありとなるためシグネチャが同じにはなりません。
この例なら @MethodSource
じゃなく @ValueSource
が適切ですが、そこはお目こぼしください。
少し脱線になりますが、ファクトリメソッドの戻り型では Stream
の他に Collection
や配列などを使えますが、JUnitとしては「Stream
にできるもの」だったりします。(MethodSourceのJavadoc参照)
MethodSource
を使うと使われるMethodArgumentProviderでtoStreamしています。で、このtoStreamの中身はStreamだったらそのまま返すなので、「配列かList
かStream
かどれにすればいいんだろ」とか 無駄に 迷った時は Stream
にしておくのがいい感じです。迷わないなら何でもいいので、無理に Stream
を使う必要はありません。あと見ての通りStream
はclose
してくれないので、閉じなきゃいけないStream
は使っちゃだめです。
ここまではおさらい。
@MethodSource
と @Nested
で注意が必要だったこと
JUnit5でネストしたテストを @Nested
で書きます。JUnit4のEnclosed
ではstatic
なクラスでネストしましたが、JUnit5ではstatic
無しです。
これにより外側のテストクラスのインスタンスフィールドが使用できるなど、色々便利になったのですが、@MethodSource
のファクトリメソッドはstatic
になりますので、組み合わせると少々面倒なことになっていました。
class MyParameterizedTest { @Nested class MyNestedClass { @ParameterizedTest @MethodSource void test(String parameter) throws Exception { assertEquals(4, parameter.length()); } static Stream<String> test() { return Stream.of( "hoge", "fuga", "piyo" ); } } }
こう書きたいのですが、非static
なクラスでstatic
なメソッドを持つことはできなかったので、コンパイルエラーになりました。エラーメッセージは以下。
.../MyParameterizedTest.java:36: エラー: 内部クラスMyParameterizedTest.MyNestedClassの静的宣言が不正です static Stream<String> test() { ^ 修飾子'static'は定数および変数の宣言でのみ使用できます
なので以下のいずれかで対応していました。
@Nested
なクラスを@TestInstance(TestInstance.Lifecycle.PER_CLASS)
とした上で、ファクトリメソッドを非static
メソッドにする。- ファクトリメソッドを
static
で宣言できる場所に移動し、@MethodSource
で指定する。ここだと外に出してMyParameterizedTest#test
とFQNで指定する。
前者は検索すると目にしますが、テストインスタンスのライフサイクルはデフォルトの PER_METHOD
であってほしいので、やるにしても嫌な気分を背負いながらになります。後者はあまり見ないかな……ライフサイクルは変えなくていいんですが、せっかく省略できるファクトリメソッド名を指定しなきゃいけない上、FQNになるので冗長だし、外に出すとテストメソッドから離れてしまうしで、コーディング上でのやりたくない理由が多いんですよね。痛し痒しです。
どちらもユーザーガイドやJavadocをちゃんと読めば書いているんですが、全部読んでられないってのも実際のところかと思います。 あまり公式ドキュメントを読み慣れていない方はこういう機会に「このブログで書いてることって、どこにどういう風に書いているんだろう」と探してみると、他のことも「あの辺に書いてるんじゃないかな」とアタリがつけられるようになるかもしれません。ならないかもですが。
若干絡むか絡まないかの公式ドキュメントの読み方の話。
必要「だった」です
本題。
ここまで「必要 だった 」とか「コンパイルエラーに なりました。」とか過去形で書いてて、微妙な日本語になっています。
これはJava17(Java12-16は既にEOLなのでJava17を採用)でJEP 395: Recordsによりstatic
メンバの定義が緩和されたためです。
興味のある方はJEP内の "Static members of inner classes" を参照ください。
これにより、上記のコードがコンパイルエラーにならなくなりました。
と言うことで、素直に動きます。「 @Nested
な @ParameterizedTest
で @MethodSource
を使う時は @TestInstance(TestInstance.Lifecycle.PER_CLASS)
にしたうえでファクトリメソッドを非static
にする」なんてことをやる必要はありません。このノウハウが必要なのはJava11を引き続き使用する場合のみです。Java17以降ではノイズになりますので、既存コードにある場合はサクッと消しておくようにしましょう。
いやぁ、ふつうに書けるっていいなぁ!と言うことでタイトルの「注意点」は「注意点がなくなった」です。
……Java17に上げられるならね。どうせユニットテストレベルだとJavaのバージョン違いでの差なんてほとんど出ないし、テストのコンパイルと実行だけ17にしていい……と思ったりしなくはない。テキストブロックもテストでは便利だし(特にJSON書くとき)。ビルドが複雑化するのを考えると微妙だけど。やっていいと思うけど、自分しかビルド環境メンテできない状況だと多分やらない。他の人がやろうとしたら応援する、くらいかなぁ。。。
書いた経緯
「なんか最近要らなくなったんだよなぁ」とふわっとした知識で話したんですが、JUnitのバージョンアップだと思ってて。でJUnitのリリースノートとか追ってみたんだけど見つからなくて。テストコード書いて見ようとしたらJava11だと書けなくて「あーそうだ、非static
には書けないよなー」と。でもJava17だと普通に書けたので、ああ緩和されたんだっけ、と。
Javaの仕様で必要だったフレームワークの使い方の迂回策が、Javaのバージョンアップでそもそも不要になっているわけです。 もちろん元のままでも動作しますが、この手のはしばらくすると理由が失われ「なんでこんなことをしているんだろう」となり、 理由がわからなくなると「消していいかどうかわからない」となると、おまじないとなって残ってしまいます。 過渡期にいる人は理由がわかるうちに対処する役割を担っているんじゃないかな、と思うわけです。
……とかそれっぽいこと書きましたが、単に「せっかく調べたから書いとこ」くらいのノリだったりします。
おまけ: IDEの警告
IntelliJ IDEAはこの辺りもいい感じに警告してくれます。
警告が出ていることに慣れているような方もいるかと思いますが、IDEの警告0を常態化することを強くおすすめします。
JUnit 5の拡張機能を完全にマスターしたぃ
本日9/19の関ジャバ'19 9月度でJUnit 5のExtensionの話をしました。
スライド
JUnit 5.5.2 のUser Guide を合わせてご参照ください。なお5.3ならoohiraさんの日本語訳もあります。とても助かってる。
ちなみに表紙で言及してるBtoWは ゼルダの伝説 ブレス オブ ザ ワイルド - Switch で、ロード画面がこんな感じなのです。
背景
9月度のセッションは、以前の関ジャバにて登壇枠で申し込みいただいたお二人です。
- 業務システムの概要とその選択肢 / tunemageさん
- みんなのSelenium:The Road to WebDriver / woosyumeさん
他のイベントでの登壇などから特に心配はないのですが、流石に初登壇の方だけにお任せするのもあれなので私も話すことにしてみました。
Seleniumに絡めてテストの話、と考えてExtensionになったわけです。 ちなみにJJUG CCC 2019 FallのCfP出して落ちたネタです。
今回は我ながら珍しく、エモさほぼなしのテクニカルのみな内容になっています。
実際
お二人のセッション共に期待していた以上でした。ありがとうございました。
結局開始直前まで資料作成してた私が一番不安要素だったというか予想通りというか、はい。
woosyumeさんが私のセッションがJUnit5だからと、スケルトンをJUnit5に更新してくれたと聞きいたのを受け、Extensionの話をするんだからとWebDriverの初期化と引数解決できるExtensionを即興で作ってみました。自分のセッションに間に合って実例を見せれました。
いいネタをありがとうございました。
BotWは面白いです。これのためだけにSwitch買って良かった。関ジャバ関係ない。
- 出版社/メーカー: 任天堂
- 発売日: 2017/03/03
- メディア: Video Game
- この商品を含むブログ (37件) を見る
2019-09-20T00:52 追記
2016-12-03に 「どうしようJUnit 5」ってセッション をしてるんですが、その65ページで「詳しい話はそのうち」って言ってたんですよね。今回のはその「詳しい話」になります。 3年弱でようやく。正直忘れてた。
#jjug_ccc 2016 FallでJUnit5の話をしてきた
今年の目標を「1ヶ月に1エントリ以上のペースでブログを書く」にします。 これで9エントリになるので後3ですね。2012年12月は1ヶ月で34エントリ書いてるので、今の私と彼が同一人物なら余裕なはず。
さて、2016-12-03のJJUG CCC 2016 Fallで話させてもらいました。 タイトルはどうしようJUnit 5です。
JJUG CCCについて
- 2011 Spring 参加: ブログ
- 2013 Spring 参加: ブログ
- 2013 Fall 不参加: キャンセルしちゃってる
- 2014 Spring 不参加: 1日寝てたっぽい。たぶんJavaDayTokyo2015で燃え尽きてた。
- 2014 Fall 参加
- 2015 Spring 参加
- 2015 Fall 参加: よくある業務開発の自動化事情
- 2016 Spring 参加: テスト自動化のまわりみち(資料非公開)
- 2016 Fall 参加: どうしようJUnit 5 ←今回
参加は6回目だと思ってたけど、7回目だった。どんどんパワフルになっている良いイベント。いい機会をいただけています。ありがとうございます。
資料
準備段階における言い訳をしてみる
1週間前からここ数年無い程度の体調不良に襲われ、滅多に仕事を休んだりしないのですが、2日間お休みしてしまったりしていました。当日は発表前に咳止めを飲んでたり。 あと、11/30にJUnit5.0.0-M3がリリースされて、いろいろと修正が必要になってたりもしてます。junit-jupiter-migration-supportとかM2にはなかったし。まあ準備できてないのはそれだけが理由じゃ無いんだけど。
そんなわけで、お聞き苦しいところもあったかと思いますが、恥じる内容では無いつもりです。
登壇のふりかえり
時間は過不足なく残り30秒程度で終えたはず。時間配分はなんとかなった。 エモ系セッションが多い私ですが、今回はテク系でやろうと思ってやりました。
感想。技術セッションは難しいなーと改めて思いました。
登壇前は「機能の単純な説明はしない。ユーザーガイドを読んでもらった方がいい。」と思ってたのですが、聞いて頂いている方々の反応を見て、機能説明の比重を増やして話しました。合ってたかどうかは知らない。
しかし長丁場のイベントで、単に知識を入れるようなセッションは負荷も大きいんだろうなと。 時間帯や私の話し方(飲み物忘れたのと風邪とで声が出づらかった・・・・・)もあったと思いますが、前回よりお疲れな方が多いように感じました。人数が多かったのもあって、室温も少し高かったのかな。
あと、デモは入れた方がよかったです。目先も変わるし。
次機会あったらデモかライブコーディングか入れたいなー。そっちの準備もしてはいたんだけど、時間が捻出できなかった。
— いろふ (@irof) 2016年12月3日
今回は時間配分的に入れられなかったけど。
結局JUnit5はどうなんよ?
発表駆動で触っただけですが、私は気に入りました。聞いた方がどうとらえたかは知りません。 リリースされたら即使うと思います。楽しみです。
蛇足: JUnit5開発にあたっては寄付金が募集されたことがあって、キャンペーンページに名前載せてもらってたりする。
宣伝
スライドの中で触れてるAssertJの記事はWEB+DB PRESS Vol.95で書いてます!!
- 作者: 小出淳子,黒澤剛志,牧大輔,横江亮佑,山口貴也,尾藤正人,佐藤琢哉,中橋研太郎,田中慎司,小西裕介,伊藤直也,稲富駿,前島真一,長野雅広,山際康貴,のざきひろふみ,うらがみ,岡林大,遠藤雅伸,ひげぽん,海野弘成,はまちや2,竹原,大場寧子,大場光一郎,野々下裕子,WEB+DB PRESS編集部
- 出版社/メーカー: 技術評論社
- 発売日: 2016/10/22
- メディア: 大型本
- この商品を含むブログを見る