質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
JUnit

JUnitは、Javaで開発されたプログラムのユニットテストを行うためのアプリケーションフレームワークです。簡単にプログラムのユニットテストを自動化することができ、結果もわかりやすく表示されるため効率的に開発時間を短縮できます。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

4回答

4427閲覧

関数型IFを返却するメソッドのjUnitでの検証

7tsuno

総合スコア310

JUnit

JUnitは、Javaで開発されたプログラムのユニットテストを行うためのアプリケーションフレームワークです。簡単にプログラムのユニットテストを自動化することができ、結果もわかりやすく表示されるため効率的に開発時間を短縮できます。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2017/02/07 04:06

###前提・実現したいこと
関数型IFを返却するメソッドの戻り値を検証したい場合、どうしたらいいでしょうか。

###発生している問題・エラーメッセージ

以下のようなメソッドがあった場合

java

1 public Function<String, String> sampleMethod(boolean flg) { 2 3 if (flg) { 4 return str -> str + "hoge"; 5 } else { 6 return str -> str + "fuga"; 7 } 8 9 }

引数がtrueの場合に戻り値が[ str -> str + "hoge" ]であることを検証したい場合
jUnitではどう検証を行えばいいでしょうか?
また、関数型IFに適したMathcerのライブラリなどをご存知の方がいればご教示いただきたいです。

###試したこと

java

1 Main target = new Main(); 2 3 @Test 4 public void test_sampleMethod() { 5 6 // Exercise 7 Function<String, String> actual = target.sampleMethod(true); 8 9 // Verify 10 Function<String, String> expect = str -> str + "hoge"; 11 assertThat(actual.apply("hoge"), is(expect.apply("hoge"))); 12 }

という検証をつくるとテスト自体はOKにはなるのですが、

java

1 Main target = new Main(); 2 3 @Test 4 public void test_sampleMethod() { 5 6 // Exercise 7 Function<String, String> actual = target.sampleMethod(true); 8 9 // Verify 10 Function<String, String> expect = str -> "hogehoge"; 11 assertThat(actual.apply("hoge"), is(expect.apply("hoge"))); 12 }

でもOKになってしまうため、正しい検証にはなっていません。

###補足情報(言語/FW/ツール等のバージョンなど)
java : 1.8.0_111
jUnit : 4.12

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

少々乱暴な方法ですが、
関数と関数が同じかを確かめる方法の一つに、
十分多い数、引数をランダム生成して、
全部通るか検証する方法があります。

"悪意を持った"実装者がよほど運が良くなければテスト通りません。


いや、特定の引数に対して変な値を返すように"悪意をもって"実装すれば高確率でテスト通りますが、
要するにテストの文面でランダム値を使うことで、
実装者に「入力としてあり得るすべての値でテスト通るようにしろ」
という意図を伝える事ができるってことです。

ただ、この程度の関数だと、質問文のコードのままで意図が十分伝わるはずので
全然問題ないことになっちゃいますがね。


※回答じゃないです

質問文だとexpectの方を書き換えちゃってますが、

java

1public Function<String, String> sampleMethod(boolean flg) { 2 3 if (flg) { 4 return str -> str + "hoge"; 5 } else { 6 return str -> str + "fuga"; 7 } 8 9 }

という実装がされることを期待して

java

1 Main target = new Main(); 2 3 @Test 4 public void test_sampleMethod() { 5 6 // Exercise 7 Function<String, String> actual = target.sampleMethod(true); 8 9 // Verify 10 Function<String, String> expect = str -> str + "hoge"; 11 assertThat(actual.apply("hoge"), is(expect.apply("hoge"))); 12 }

というテストを書いたが、
これだと

java

1public Function<String, String> sampleMethod(boolean flg) { 2 3 if (flg) { 4 return str -> "hogehoge"; 5 } else { 6 return str -> str + "fuga"; 7 } 8 9 }

みたいにいくらでも抜け道ができて困る
ということですよね?


余談

str -> str + "hoge"自体のテストを抜け道潰して行おうとすると
exceptで同じ関数を実装することになって意味がなくなります。
こういう場合はProperty based testというものを行います。

f(x).substring(0,x.length()).equals(x);

みたいな関数それ自体の性質を何個か作り、それをテストします。

投稿2017/02/07 04:35

編集2017/02/07 06:50
ozwk

総合スコア13512

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

7tsuno

2017/02/07 04:38

そういうことです。もともと返却値が関数のようなものなので試験なんて出来ないんでしょうかね。
7tsuno

2017/02/07 06:02

やはり返却された関数も同様に試験的なことをしないと無理ですよね。 この場合は簡潔ですが、返却された関数が複雑なメソッドの場合二重に試験を行わなければいけなくなりそうですね。
yuba

2017/02/08 00:15

やることは同じでも、「二重に試験を行う」という発想をしなくてもsampleMethodは、実のところ、flag, strを取る二引数関数をカリー化したものだといえます。そうしたらsampleMethodとその戻り値をあわせて二引数関数と捉え、単に二引数関数をテストするのだと考えればそれほどおかしいこと、二重に試験をしているというわけではないと言えます。
7tsuno

2017/02/08 00:51

一つの関数として考えればっていうことですね。 その考え方もありですね。勉強になります。
guest

0

私ならこう書くかなぁ。
実際のテストでは特にこっちの方がわかりやすいとおもう。

java

1 Main target = new Main(); 2 3 @Test 4 public void test_sampleMethod() { 5 6 // Exercise 7 Function<String, String> actual = target.sampleMethod(true); 8 9 // Verify 10 assertThat(actual.apply("hoge"), is("hogehoge")); 11 assertThat(actual.apply("fuga"), is("fugahoge")); 12 }

追記

実際に近い命名をしてみました。

戻り値のFunctionはHogeを連結する関数型なので、
テストする内容はHogeが連結されていることになります。

意図が明確になると違和感がないと思います。

java

1 Main target = new Main(); 2 3 @Test 4 public void test_joinHogeOrFuga() { 5 6 // Exercise 7 Function<String, String> joinHoge = target.joinHogeOrFuga(true); 8 9 // Verify 10 assertThat(joinHoge.apply("hoge"), is("hogehoge")); 11 assertThat(joinHoge.apply("fuga"), is("fugahoge")); 12 // 連結をさらに強調したいなら 13 assertThat(joinHoge.apply("hoge"), is("hoge" + "hoge")); 14 assertThat(joinHoge.apply("fuga"), is("fuga" + "hoge")); 15 }

テストは結果や意図が明確であることが大事だと思います。

投稿2017/02/07 05:21

編集2017/02/07 05:49
iwamoto_takaaki

総合スコア2883

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

7tsuno

2017/02/07 05:59

やはり返却された関数をさらに試験するようにしないとだめそうですね。こちらで期待した関数とアサーションできないかと考えていたのですがそれは無理そうですよね。
iwamoto_takaaki

2017/02/07 06:27 編集

関数とアサーションする必要があるかどうかというのがポイントになりそうです。 私が例題の関数は引数によって文字列を装飾してくれるメソッドなので、装飾されていること、それが一目でわかるテストというのが大事だと思います。 もちろん境界条件などのテストでわかりずらいテストケースも必要ですが、基本のテストは、仕様の説明であるべきで、基本型でチェックを基本とすべきと考えています。 基本型にすべき理由としては、わかりやすさが一番にありますが、exceptが計算式やメソッド(この場合は式)の場合、中身はチェック対象と全く同じになるため、exceptにバグを作る可能性があります。 また、例の場合は特に、チェック対象のメソッドと同じものを作成しているので、テストしていないのと同じになってしまいます。 まあ個人的な見解ですが。。。
7tsuno

2017/02/07 08:40

意味のあるメソッドであればそうですね。 一般的な場合で質問をしたかったので簡単な例を出したのですが、確かにメソッドの意味を考えてテストをするというのもひとつの方法であるかもしれませんね。 expectをまったく同じ形にしたのはこの条件のときにこの式を返すとしたかったわけです。 やはりそういう形での検証は出来なそうですね。
iwamoto_takaaki

2017/02/07 09:10

できるできない以前に、メソッドのテストの場合と同じように戻ってきたFunctionのテストをすれば十分だとおもいます。メソッドと同様に複雑なFunctionはありえますから・・・ とはいえ、必要なパターンも含め、このような純粋な思考も面白く思います。
7tsuno

2017/02/07 09:19

もちろん理解はしていますが、 複雑なFunctionが返ってきた場合は結局そのFunctionの試験もしなければならないのが億劫ですね。 そのメソッド単体の試験としては「複雑なFunctionが返却された」ということだけ検証できればいいと思っているので、 assertThat(actual, is(str -> すごく複雑なメソッド(str))); のような形でできたらなあという妄想です。
iwamoto_takaaki

2017/02/07 12:34

そうです。ユニットテストにすごく複雑なメソッドを実際に書くことになり、現実的ではないですよ。テストにバグを作ります。 まともな設計でまともな粒度のテストを書けば、そんな複雑なテストは不要です。逆を言えば、複雑なテストが必要になる時点で設計を間違っていると考えて問題ありません。
guest

0

関数なので単一の値のテストではどのみち不十分ですね。だからといって沢山やる必要もないですが、こんなふうに一々書くことの手間を減らせれば充分という気がしました。

assertThat(actual.apply("foo"), is(expect.apply("foo")));
assertThat(actual.apply("bar"), is(expect.apply("bar")));
...

JUnit初心者なので何が適切かは全然自身ないですが。以下のようなマッチャーを定義してそれを使うといったアイデアが浮かびました。

<T, R> isFor(Function<T, R> function, T... values);
->
assertThat(actual.apply("hoge"), isFor(expect.apply("hoge"), "foo", "bar"));

それとも既に定義されているんでしょうか・・・もしこのようなものがなくても、値のパターンを指定するJUnitの機能が別途あった気がするのでそちらを使う方がよくて、自分でマッチャーを定義する手間をかける必要はないかも知れません・・・

投稿2017/02/07 05:18

編集2017/02/07 05:20
KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

7tsuno

2017/02/07 06:01

マッチャーを定義して値をたくさん入れてっていうのは良さそうですね。 自動で文字列を生成してランダム文字列を指定回数分チェックとかもできたら整合率もかなり高くできそうですね。
ozwk

2017/02/07 10:03

jPopulatorなんてどうでしょう
7tsuno

2017/02/07 10:12

これ面白そうですね!いままで乱数で自分で作ってました...
guest

0

Verifyで書かれている

java

1Function<String, String> expect = str -> "hogehoge";

の期待する動き(何を入れても hogehoge が得られる)と、テスト対象の関数 ( trueのときは、入力した値に hoge を追加するので hogehoge が得られる ) が同じ値となる検証なので、
expect(期待値)の書き方次第で 検証結果がtrueとなってしまうだけです。

検証してる内容が正しくないように思えますが、いかがでしょうか。

投稿2017/02/07 04:25

A-pZ

総合スコア12011

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

7tsuno

2017/02/07 04:35

それは分かっています。この検証だとNGなのでどう検証をすればいいのかというのが今回の質問です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問