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

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

新規登録して質問してみよう
ただいま回答率
85.44%
ユニットテスト

ユニットテストは、システムのテスト手法の一つで、個々のモジュールを対象としたテストの事を指します。対象のモジュールが要求や性能を満たしているか確認する為に実行します。

Q&A

解決済

2回答

1497閲覧

多態性(ポリモーフィズム)のパターンを単体テストする場合

matari

総合スコア40

ユニットテスト

ユニットテストは、システムのテスト手法の一つで、個々のモジュールを対象としたテストの事を指します。対象のモジュールが要求や性能を満たしているか確認する為に実行します。

0グッド

2クリップ

投稿2022/08/18 06:36

多態性を用いる際、意図通りのインスタンスができているかどうかを確認するための単体テストはどのようにすればよいでしょうか?

例えば、IAを継承するA1、A2、A3クラスがあって、IAを持つクラスBがあった場合、
クラスBのIA型のプロパティのインスタンスがA1なのかA2なのかA3なのかを判別したいけれど、
多態性によってそれらが外部から基本的に区別がつかない状態になっています。

もちろん、それぞれを区別できるような動作を敢えて行えばわかるのですが、
例えば、テスト用にインスタンスの型を識別できるようなプロパティを作ったり、
もしくは、リフレクションを使って識別したりするのは邪道でしょうか?

因みに、A1、A2、A3は個別に単体テストを行なっています。
あくまでも、その生成パターンが意図通りかというテストを行なうことのみを目的としたいです。

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

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

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

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

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

maisumakun

2022/08/18 06:59

> クラスBのIA型のプロパティのインスタンスがA1なのかA2なのかA3なのかを判別したいけれど なぜ判別したいのですか?
matari

2022/08/18 07:06

パターン先の動作(A1、A2、A3)は個別にテストを終えているので、 次はそのパターン分岐のテストをしたいと思っています。
maisumakun

2022/08/18 07:08

> 次はそのパターン分岐のテストをしたいと思っています。 そのテストがなぜ必要なのでしょうか?「どれを返しても構わない」から総称の型を使う、というのがポリモーフィズムなのではないでしょうか。
matari

2022/08/18 07:20

どの状態でも、どれを返しても構わないわけではありません。 それぞれの条件下で、異なるインスタンスを返す必要があるけれども、 外部から見たときにそれらを区別する必要がないのが多態性の役割だと思います。 たまたま問題ないパターンがあるだけで、すべてにおいてそれが言えるとは思えません。 例えば、本番の環境での動作中にテスト用のMockインスタンスが渡ると困ると思います。 外部から見れば区別はつかないですが、内部では処理が異なります。 意図通りの状態で、意図通りのインスタンスを返さないと意図通りに処理できません。 もちろん、この前提が間違っていれば、そもそも私のテストそのものに意味はないのですが…。
maisumakun

2022/08/18 07:33

> 例えば、本番の環境での動作中にテスト用のMockインスタンスが渡ると困ると思います。 それはポリモーフィズムとは別問題かと思います。
matari

2022/08/18 22:12

例えの話です。 別にMockでなくとも、意図していないインスタンスが渡ると問題ではないですか?
guest

回答2

0

意図通りの状態で、意図通りのインスタンスを返さないと意図通りに処理できません。

では、意図通りのインスタンスでなければ正しく動かない処理が正常に動作するかをテストすればいいだけではないでしょうか。

(そういう差異がまったくないのであれば、どのインスタンスを返すかの検証は不要、ということになります)

投稿2022/08/18 07:32

maisumakun

総合スコア145360

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

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

matari

2022/08/18 22:13

動作についてはすでに個別で行なっているので、 テストが二重になってしまうことについて疑問に思っています。
maisumakun

2022/08/18 22:20

二重ではありません。「こういう条件ではこのようなインスタンスを返す」ということのテストです(テストに通るまま、同じ動作をする別のインスタンスを返すことも可能です)。
matari

2022/08/18 22:29

目的が異なっても、動作を確認するので、個別で行なっているテストと コード上は重複するようになってしまいます。 目的はコードに現れないので、結局二重化になってしまいます…。
maisumakun

2022/08/18 23:30

テストコードの重複はあまり気にする必要はありません。同じことをやっていても意味は異なります。
matari

2022/08/19 02:06

なぜテストコードの重複を気にする必要がないのか、よくわかりません…。 同じ様なテストコードがあるということは、テストコードが適切ではない、同じ機能が別途実装されているもしくは不適切な実装がされているなどの問題を発見する糸口になると思います。 また、今回もベストアンサーにたどり着けたということで、重複への違和感は大切かなと思います。
maisumakun

2022/08/19 02:43 編集

「型としてはインターフェースAとなっている、あるメソッドの返り値がある性質を持つ」ことと、「インターフェースAを実装したクラスA2がある性質を持つ」ことは、本質的に全く無関係です(「この場合はこのクラスを返す」こと自体が動作仕様という状況でなければ、前者の返り値を、同じ振る舞いをするA2'のインスタンスに差し替えてしまうことも可能です)。 型理論的に言えば、「ある性質を持つ」クラス用にインターフェースAを継承した別のインターフェースを作って、「返り値がそのインターフェースになる」ことと「A2がインターフェースの要件を満たすこと」を検証する、というのが密結合を避けられてきれいに実装できるかと思います。
matari

2022/08/19 04:53

「BクラスのIA型のプロパティのインスタンスが意図通りか」をテスト対象としています。 例えば、IA型にはDisplay()という関数があって、特定の文字列を画面に出力するとします。 (今回は出力先の話は無視し、文字列のみに着目するとします) IA型にA1のインスタンスが格納されている場合と、A2のインスタンスが格納されている場合、A3のインスタンスが格納されている場合ではそれぞれ出力される文字列は異なります。 そしてそれは、「Bクラスの状態」によって異なります。 単純化するために、Bクラスのコンストラクタに数値を入れることによって分岐するとします。 その数値が「1」の場合A1、「2」の場合A2、その他ではA3とします。 もし、それらの分岐が意図通りでない場合(例えば「1」の場合A2、「2」の場合A3、その他ではA1など)、つまり「インスタンスが異なる場合」、問題が起きます。 (蒸し返すようですが、「そのテストがなぜ必要なのでしょうか?「どれを返しても構わない」から総称の型を使う、というのがポリモーフィズムなのではないでしょうか。」に対する返答です) なので、これらをテストして問題なく分岐できていることを確認することが目的となります。 もちろん、冒頭の質問に書いてある通り、動作で確認することもできるのですが、 動作して確認する場合、何が共通動作でないのかを気にしながら書く必要もあり、また変更に対しても影響を受ける(手間が増える)ようになります。 本来のテストの目的は「インスタンスの型の区別」なので、一番直接的な型のキャストが一番適切かなと思います。 こういったことから、テストコードの重複(今回でいえば動作)というのは、直接的な損害はないにせよ、改善のきっかけとなる、いわゆる「コードの臭い」のようなものなのかなと思いました。 >「型としてはインターフェースAとなっている、あるメソッドの返り値がある性質を持つ」ことと、「インターフェースAを実装したクラスA2がある性質を持つ」ことは、本質的に全く無関係です(「この場合はこのクラスを返す」こと自体が動作仕様という状況でなければ、前者の返り値を、同じ振る舞いをするA2'のインスタンスに差し替えてしまうことも可能です)。 まず、目的に相違があるのかなと思いました。 そもそもインターフェースの型が同じでも、動作は異なることが多いです。 というより、ポリモーフィズムの目的がそれにあたると思います。 なので、型が一緒であればインスタンスが異なってもよいというのは、使用者側の理屈であり、テストでそれらを区別する必要がある場合には通用しない理屈になります。 なので、インスタンスの区別をする必要があります。 >型理論的に言えば、「ある性質を持つ」クラス用にインターフェースAを継承した別のインターフェースを作って、「返り値がそのインターフェースになる」ことと「A2がインターフェースの要件を満たすこと」を検証する、というのが密結合を避けられてきれいに実装できるかと思います。 こちらの目的は「適切なインスタンスである」と「型毎に動作が仕様通り」の2種類のテストをしています。 インターフェースは関係ありません。
maisumakun

2022/08/19 05:23

「この場合はこのクラスを返す」こと自体が動作仕様という状況、だったのですね。了解しました。
guest

0

ベストアンサー

「Bが持つIAが実際はAnだ」をテストするのではなく
「Bが持つIAはこう動くはずだ」をテストしてください。

投稿2022/08/18 06:50

ozwk

総合スコア13544

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

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

matari

2022/08/18 07:08

回答ありがとうございます。 動作自体は個別でテスト済みですので、パターン分岐が意図通りかどうかを確認したいです。 例えば、プロパティを持つクラスで状態を確認するような感覚でテストしているのですが 的外れなのでしょうか?
ozwk

2022/08/18 07:25

「この場合はA1を保持する、この場合はA2を...」というのが仕様である(仕様とする)ならば キャスト可能かどうかを調べるなどすればいいと思います。
matari

2022/08/18 22:17

なるほど! キャスト可能かどうかが一番よさそうなテストですね! 回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.44%

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

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

質問する

関連した質問