ポケットのなかにはビスケットがひとつ
ポケットをたたくとビスケットはふたつ
もひとつたたくとビスケットはみっつ
たたいてみるたびビスケットはふえる
そんなふしぎなポケットがほしい そんなふしぎなポケットがほしい
ポケットを叩くと、お金が増える、そんな不思議なポケットが欲しい~♪
という...はい、どうもボクです。
というわけで、今回もJavaのお話です。
そして、
⇧ 上記サイト様にありますように、長時間労働で成長するわけじゃないのね(涙)。とりあえず、次の会社では、もう少し、成長できるように頑張ろうと思う今日この頃です。研修は1ヵ月だけみたいだけど、現場にはチームで配属って言ってたし、即戦力でなくとも大丈夫って仰っていたし、多少は気が休まるのかな~と。
そんでは、レッツトライ~。
JavaのリフレクションとJunitのコラボの罠
というか、こんな現象が起こりえるんだという事実に震撼しました。ことの発端は、職場の同僚の方が、単体テスト してた時に、
で検証結果が異なるという事態に直面。っていうか検証ツールで、実行結果が異なるってアカン気がするけど...JUnitさん。
結論から言ってしまうと、タイトルでも言った通り、「カバレッジ実行」と「実行」で使用されてるメソッドが異なっていたらしく、リフレクションでメソッドを実行した時に発覚したという。
つまり、リフレクションの使用条件とJUnitの組み合わせが生み出した悲劇(涙)。まぁ、Eclipseのプラグインが影響していたんですけどね...。
というわけで、再現してみる
実際に、どういうことが起こっていたのか、「百聞は一見に如かず」ということで、見た方が納得できるのではなかろうかということで、試してみますか。
JUnit は、 Javaの標準APIではないので、外部からインストールする必要がありますんで、今回は、Eclipseで、Gradleプロジェクト作成で。(最近のEclipseはデフォルトでJUnitが入っているようでした...)
⇩ ちなみに、ビルドツールは、「Gradle」が人気なんですかね?
・http://sassembla.github.io/Public/12:05:27%2018-03-20/12:05:27%2018-03-20.html
話が脱線しましたが、Eclipse を起動し、「ファイル(F)」>「Gradle プロジェクト」で。
「パースペクティブ」を「Java EE」にしてますが、Gradleプロジェクトができました。
「プロジェクトと外部の依存関係」に「junit-×.××.jar」ってあれば、JUnitが使えますかね。
だが、しかし!
対象のプロジェクトでコード・カバレッジを使用可能にします。 対象のプロジェクトを 右クリックし、を 選択します。 「コード・カバレッジを使用可能にする (Enable code coverage)」を選択します。 「OK」をクリックします。
Eclipse側で設定を有効にする必要があるらしい。
プロジェクトを選択した状態で、「プロパティー(R)」で。
ん?コードカバレッジが存在しないんだが...
⇧ 「EclEmma」ってEclipseプラグインが必要だったようです...知らんがな(涙)
というわけで、インストール...しようと思ったら、「マーケットプレイス」が選択肢として表示されないんですけど...。
⇧ 上記サイト様を参考にさせていただきました。
⇧ 上記でご自分のお使いのEclipseのバージョンのURLをコピーします。自分の場合は、「http://download.eclipse.org/mpc/2018-09/?d」ってなってたので、「http://download.eclipse.org/mpc/2018-09」に整えました。
Eclipseのバージョンとかは、「ヘルプ(H)」>「Eclipse IDE について(A)」で。
というわけで、「ヘルプ(H)」>「新規ソフトウェアのインストール...」で。
「対象作業(W):」にコピペしておいたURLを張り付け、「追加(A)...」で。
「名前(N):」を適当に入力し、「追加(D)」で。
「EPPマーケットプレイス・クライアント」にチェックし、「次へ(N)>」。
なんか、上手くいってなさそうだけど...インストールしないと「マーケットプレイス」使えないんでね。「次へ(N)>」。
「次へ(N)>」。
「完了(F)」で。
再起動で。
「Eclipse マーケットプレイス(M)」が復活しました。
「検索(I):」で、「EclEmma」で。表示された「EclEmma Java コード・カバレッジ」を「インストール」で。
「確認(C)>」で。
「完了(F)>」で。
再起動で。
「カバレッジ(V)」>「JUnitテスト」が選択できるようになりました。
そんでは、デフォルトで用意されている、「Library.class」に実際の処理を、「LibraryTest.class」にテストコードを記述します。それとは別に、今回、もう1つクラスを追加します。
GitHub にソースを上げたので、宜しければご参照ください。
プロジェクト構成は、こんな感じ。
「プロジェクトと外部の依存関係」には、「junit-x.xx.jar」「hamcrest-core-x.x.jar」があれば問題ないかと。
んで、「カバレッジ(V)」>「JUnitテスト」で実行。注意する点としては、テストクラス(「src/test/java」のほう)で実行するってとこですかね。
んで、「コンソール」には、
private static boolean[] dto.Jyugemu.$jacocoInit() public void dto.Jyugemu.setName03(java.lang.String) public java.lang.String dto.Jyugemu.getName02() public java.lang.String dto.Jyugemu.getName05() public void dto.Jyugemu.setName10(java.lang.String) public java.lang.String dto.Jyugemu.getName08() public void dto.Jyugemu.setName11(java.lang.String) public java.lang.String dto.Jyugemu.getName11() public void dto.Jyugemu.setName05(java.lang.String) public java.lang.String dto.Jyugemu.getName01() public java.lang.String dto.Jyugemu.getName03() public java.lang.String dto.Jyugemu.getName06() public void dto.Jyugemu.setName07(java.lang.String) public void dto.Jyugemu.setName08(java.lang.String) public void dto.Jyugemu.setName01(java.lang.String) public java.lang.String dto.Jyugemu.getName09() public java.lang.String dto.Jyugemu.getName10() public void dto.Jyugemu.setName09(java.lang.String) public void dto.Jyugemu.setName02(java.lang.String) public java.lang.String dto.Jyugemu.getName04() public void dto.Jyugemu.setName04(java.lang.String) public void dto.Jyugemu.setName06(java.lang.String) public java.lang.String dto.Jyugemu.getName07() ----- 結果 ----- setName01=寿限無 setName02=寿限無 setName03=五劫ごこうの擦すり切きれ setName04=海砂利かいじゃり水魚すいぎょの setName05=水行末すいぎょうまつ・雲来末うんらいまつ・風来末ふうらいまつ setName06=喰う寝る処ところに住む処 setName07=藪やぶら柑子こうじの藪柑子 setName08=パイポ・パイポ・パイポのシューリンガン setName09=シューリンガンのグーリンダイ setName10=グーリンダイのポンポコピーのポンポコナーの setName11=長久命ちょうきゅうめいの長助
んでは、「実行」>「JUnitテスト」で実行では。
public java.lang.String dto.Jyugemu.getName07() public void dto.Jyugemu.setName08(java.lang.String) public java.lang.String dto.Jyugemu.getName09() public void dto.Jyugemu.setName09(java.lang.String) public java.lang.String dto.Jyugemu.getName10() public java.lang.String dto.Jyugemu.getName08() public java.lang.String dto.Jyugemu.getName06() public void dto.Jyugemu.setName06(java.lang.String) public void dto.Jyugemu.setName07(java.lang.String) public void dto.Jyugemu.setName10(java.lang.String) public java.lang.String dto.Jyugemu.getName11() public void dto.Jyugemu.setName11(java.lang.String) public java.lang.String dto.Jyugemu.getName01() public java.lang.String dto.Jyugemu.getName02() public void dto.Jyugemu.setName02(java.lang.String) public java.lang.String dto.Jyugemu.getName03() public void dto.Jyugemu.setName03(java.lang.String) public void dto.Jyugemu.setName04(java.lang.String) public java.lang.String dto.Jyugemu.getName05() public void dto.Jyugemu.setName05(java.lang.String) public void dto.Jyugemu.setName01(java.lang.String) public java.lang.String dto.Jyugemu.getName04() ----- 結果 ----- setName01=寿限無 setName02=寿限無 setName03=五劫ごこうの擦すり切きれ setName04=海砂利かいじゃり水魚すいぎょの setName05=水行末すいぎょうまつ・雲来末うんらいまつ・風来末ふうらいまつ setName06=喰う寝る処ところに住む処 setName07=藪やぶら柑子こうじの藪柑子 setName08=パイポ・パイポ・パイポのシューリンガン setName09=シューリンガンのグーリンダイ setName10=グーリンダイのポンポコピーのポンポコナーの setName11=長久命ちょうきゅうめいの長助
と。
おわかりいただけただろうか...
何故かは分からんのですが、『private static boolean[] dto.Jyugemu.$jacocoInit()』っていう奴が、「カバレッジ(V)」>「JUnitテスト」では、Jyugemuクラスのメソッドに追加されてしまっているのである!
何これ~、怖い~!
おそらく、カバレッジを実行するためのEclipseのプラグイン「EclEmma」が影響しているのであろうと。
信じるか信じないかは、貴方次第~。
なので、リフレクションを使用しているクラスの単体テストにはお気をつけあそばせ。
今回はこのへんで。