web-dev-qa-db-ja.com

デイジーチェーン方式のユニットテストの哲学?

問題文:

いくつかの検証を行うクラスがあり、それは次のようになります。

_func a() {b()}
func b() {c()}
func c() {d()}
func d() {e()}
func e() {return}
_

これは簡略化されたビューですが、考えは、これらの関数呼び出しはそれぞれにいくつかの変換と検証を行っているため、相互に依存しているということです。

質問:

私がfunc a()をテストしている場合、そのメソッド内の他のすべての関数呼び出しをモックするか、only私がこれ以外の依存関係をモックする必要がありますクラス全体?たとえば、データベース呼び出しのモックなど。

8
John Lippson

ここでの実際の問題は、コードからすべての意味を単純化したことです。あとは構造表現だけです。意味、コンテキスト、またはセマンティクスを考慮する必要なく、構造ベースのルールを盲目的に実行できるように、構造に基づいて回答してください。

そのようなルールがあったとしても、それを知りたくありません。私はそれがそうでないことを知っており、ずっと前に見なくなった。

しかし、あなたがこの質問をしているのであれば、私はあなたの頭がどこにあるか知っていると思いますので、いくつかの良い方向にあなたを指摘させてください:

Func a()をテストしている場合

テストが必要な場合は、それを使用するコードを実行してa()をテストできます。正直なところ、あらゆるコード。これは手動で行うことができます。実際に自動テストを作成したい場合は...

そのメソッド内の他のすべての関数呼び出しをモックする必要がありますか、それともこのクラス外にある依存関係のみをモックする必要がありますか?たとえば、データベース呼び出しのモックなどです。

速度

0.01秒かかるテストと3分かかるテストを一緒に実行すると180.01秒かかるため、どれほど悲惨なことか考えてみてください。

あなたがテストに関する記事を読んでいて、彼らがどのようにしてテストがデータベースにヒットするのを止めたかについて自慢しているとき、レッスンは「テストでDBにヒットすることは決してない」のではないので、私はこれを持ち出します。それは「私のDBが遅かったので、他のものをテストするときにそれなしでどうすればよいかを考え出した、あなたもそれを行うことができる」です。

したがって、a(b(c(d(e()))))で何をするかを私に尋ねると、私の答えはすべてテストですが、遅いものを模擬し、それらの高速テストを別の山に置きます。

何らかの理由でd()が遅い場合(DB、ファイルシステム、素数計算機、またはランダムに生成される溶岩ランプのウェブカム)、d()をモックします。

遅いとは、a()b()c()e()が比較すると非常に速いことを意味します。彼らがあなたの「毎回私を走らせなさい」テストパイルに住むほどに。

d()をモックするポイントは、DBを嫌う悪のテストを使用することではありません。それはあなたが他のものを素早くテストするのを妨げているということです。

これらの高速テストは非常に便利なので、多くのIDEで何もクリックせずにテストを実行できます。コンパイラエラーのためにヒントや下線を付けるときにIDEのコンパイラが行うように、入力中に実行されます。高速テストがコンパイラの一部になるようなものです。

コンパイラー・エラーも速いので、なぜそれらが速い山になってはならないのですか?

遅いテストを締め出すだけです。私はあなたのことは知りませんが、タイプしている間待つのは嫌です。

読みやすさ

私は小さな孤立したテストを支持しています。しかし、すべてのクラスとメソッドには独自のテストが必要だと私が信じているからではありません。私は実際にその考えが嫌いです。分解を阻止します。いいえ、必要なのは、a()を理解するために読む必要があると感じることを制限する小さなテストです。それだ。私のテスト境界はすべてコードの読み取りに関するものです。それは構造ではありません。読み方が遅くなるので、私はテスト速度のみを気にします。それはすべて読むことです。どうして?簡単に読めればうまくいくかどうかわかります。テストを追加して、コードの読み取りを高速化します。

b()をテストするときにa()をモックする必要がありますか?両方が高速である場合、a()b()が混在していることを理解するのはどのくらい難しいですか。読みやすさが要求しない限り、b()をあざけることはしません。つまり、a(b(c(dMock(e()))))をテストすれば、初心者がそれを実行できることが確実であれば、それで完了できます。彼らができるまでそれを小さく分解し続けることができない場合。常に初心者を考慮してください。

ここで注目に値する一種の構造的な考慮事項がありますが、それでも実際には読みやすさの問題です。公共のものには多くのクライアントがいますが、私物にはほとんどありません。私はテストのためにb()を分離し、それが公開されている場合はその使用目的を明確にする可能性がはるかに高くなります。それは構造的に聞こえますが、理由は次のとおりです。それを使用するコードのすべてのビットをチェックする必要がある場合、それは読むのに大量のコードです。しかし、プライベート関数はそのプレッシャーの多くを取り除きます。

関数

私はそれらが関数であるという事実を本当に無視していることに気づいたかもしれません。それらが関数、オブジェクト、クロージャー、またはサブルーチンであるかどうか、私は本当に気にしません。 a()を書き換えて、関数なしでこのすべてを行うことができます。重要なのは、テストの速度と可読性を分離することです。 a()自体をより多くの関数に分割すると、それが可能になります。

テストの種類

ユニットテストや統合テストを何も呼び出さなかったことにも気づいたかもしれません。これは、これらの名前について十分な矛盾する説明を読んだため、それらを使用するときの本当の意味を説明するのにうんざりしているためです。遅いテストパイルと速いテストパイルがあります。私が気にするすべてのものにアリスとボブという名前を付けることができます。

ええと、私はそれを取り戻します。何をするにしても、それよりも良い名前を付けてください。

10
candied_orange

Func a()をテストしている場合、そのメソッド内の他のすべての関数呼び出しをモックする必要があります

関数aをテストする場合は、bをモックするだけです。これは、bの依存関係チェーンがaのテストにとって重要ではないことを意味します。

あなたがそうしなかったなら、あなたはAND b、c、dなどをテストしているでしょう。これはaをテストすることと同じではありません

または、このクラスの外部にある依存関係のみを完全に模擬しますか?たとえば、データベース呼び出しのモックなどです。

を含むクラスをテストする場合は、クラス外から依存関係を模擬する必要があります。

そうでない場合は、クラスとその依存関係をテストしています。

理想的には、可能な限り最小の「ユニット」のテストが必要な場合、これは通常、個々のメソッドではなくクラスであると言います。関数型プログラミングは別として、OOPまたはADM +サービススタイルのプログラミングを想定しています。

理論的には、すべての「ユニット」がテストされ合格すると、プログラムは機能します。

実際には、一緒に動作するものもすべてテストする必要があります。

これらの「統合」テストを実行し、すべてが合格すると、プログラムも機能します。

単体テストの利点の1つは、問題の場所がわかることです。

統合テストの利点は、プログラムが実際にかどうかを通知することです。

両方を実行する必要があります。

2
Ewan