反変検定

こんにちは、Habr! 記事Contra-varianceの翻訳を紹介します







翻訳者から:正直に言うと、テスト設計に関して相互共反語の選択は少し奇妙です。 もちろんセマンティクスは追跡できますが、非常に比meta的です。 おそらく、赤い言葉と注目を集める見出しのためだけに、あまり口論しないでください。 残りは対話形式のTDD関連のすばらしい記事です。 なぜTDDがそんなに苦痛なのか、ユニットテストから素敵なツールを作る方法、そして表現の自由に対する暴力を必ずしも破壊するものとして扱わない方法について説明されています。







単体テストを書いていますか?







もちろん!

最初にテストし、次にコードしますか?







はい、 TDD3つのルールに従います。

テストモジュールとコードの構造に違いはありますか?







コード内のクラスごとに1つのテストクラスを作成しています。

つまり、メインコードのクラスがUserという名前の場合、テストが行​​われます。

UserTestと呼ばれるクラス?







はい、ほとんど常に。

テスト構造はコード構造と共変であることがわかりますか?







まあ、私はそう思う。

それでは、テスト構造をコードにバインドしますか?







接続されているとは思っていませんでしたが、明らかにそうです。

そして、動作に触れずにコードクラスの構造をリファクタリングすると、

テストが壊れる?







はい、そうです。

したがって、リファクタリング中にテストを実行できませんか?







どうしてですか?

リファクタリングは壊れない一連の小さな編集であるため

テスト。







まあ、定義に基づいて、それは本当にリファクタリングではありません。

マイナーな編集の代わりに、1つの大きな変更を加えることを強いられ、希望します

その後、テストを含むすべてを収集します。







はい、それで何?

これは、壊れやすいテスト問題の例です。







壊れやすいテストの問題?

はい、TDDを初めて使用する開発者によくある苦情です。

彼らは、コードへの小さな変更が重要なことにつながることに気づきます

テストの改訂。







確かに、非常に迷惑です。 最初に遭遇したときにほぼTDDを投げた

問題。

残念ながら、これは一般的な反応です。







そして何をすべきか?

テストの構造は、コードと矛盾する必要があります。







反変?

はい、テストの構造はコードの構造を反映すべきではありません。 という事実から

いくつかのクラスはXと呼ばれ、テストの外観は

XTestという名前。







しかし、待ってください、それはルールによるものではありません!

ルールは何ですか?







クラスごとに適切なテストが必要です。

そのようなルールはありません。







どうして? 私は間違いなく彼について読んだ。

読んでいるすべてがルールではありません。







さて、もしテストの構造が反変でなければならないなら、どうやってこのようにするのでしょうか?

最初に簡単な事実を決めましょう。 1つのモジュールにわずかな変更がある場合

システムは他のコンポーネントに大きな変化をもたらし、その後システムは

悪いデザイン。







ソフトウェア設計101が同じことを言っているのは明らかです。

したがって、コードの小さな変更が大きな変更につながる場合

テストでは、これも設計上の問題です。







アイデアは明確です、私は同意します。

したがって、テストには独自の設計が必要です。 彼はただ繰り返すことはできません

メインコードの構造。







うん つまり、デザインが同じ場合、それらは接続され、接続性がリードします。

脆弱性に。

まさに。 テストとコードの一貫性は最小限でなければなりません。







やめて! ただし、テストとコードは同じものを記述するため、関連している必要があります

動作。

確かに、それらの動作は接続されていますが、これはコード構造の一貫性を意味するものではありません

およびテスト。 そして、行動のつながりさえあなたほど強くないはずです

あなたが思う。







例がありますか?

新しいクラスの作成を開始するとします。 それをXと呼びましょう。そして、私は新しいテストをしています。

XTestという名前。







しかし、あなたは「これをしないで」と言っただけです。

イベントに先んじてはいけません、まだ始まったばかりです。 XTestにさらにテストを追加すると、

Xに新しいコードを追加しています。







そして、コードをリファクタリングしてください!

当然。 元の関数からプライベートメソッドを抽出することにより、

XTestで呼び出されます。







そして、テストをリファクタリングしますか?

そう! 私はXTestとXの間の接続性を見て、それを最小化するように働きます。

これは、パラメーターをコンストラクターXに追加するか、上げることで実行できます。

引数抽象化レベル。 あるいは、ポリモーフィックインターフェイスの導入

XTestとXの間(1)







そして、これはテストを書くためだけですか?

別の方法で見てください。 XTestはXの最初の顧客です。

クライアントとサーバー間の接続を減らします。 したがって、私は同じものを使用します

通常のコードの一貫性を低下させるために適用可能な技術。







ただし、テストの構造はコードの構造を繰り返します。 XおよびXTest

どこにも行っていません。

はい、クラスレベルでは同じですが、変更されます。 しかし、あなたが

メソッドのレベルにはすでに大きな違いがあります。







実際、XTestはパブリックXメソッドを使用するだけで、主要部分は

ハイライトしたプライベートメソッドのコード。

そう! 構造的な対称性は破られますが、私はさらに破ります。







どう?

私がプライベートメソッドに目を向けるとき、私は必然的に見始めます

それらをクラスにグループ化する方法。 メソッドのグループが使用する場合

フィールドXのサブセット、それは別のクラスに区別できます。 (2)







しかし、新しいテストを作成しませんか?

はい! より多くの関数が割り当てられ、より多くのクラスが割り当てられます

検出されました。 しばらくすると、クラスのファミリー全体ができます。

シンプルなAPI Xの後ろに座っています。







そして、それらはすべてXTestでカバーされます。

そう! 構造はほぼ完全に独立しています。 また、API Xは徐々に

非常に純粋で抽象的になり、最小限の接続になります

XTestを含む顧客と。







なるほど。 テスト構造は、独立して進化できるようです

コードと私はこれが良いことに同意します。 しかし、彼らはまだ行動についてはどうですか

その後、行動によって強く関連します。

Xの開発プロセス中に何が起こるか考えてください。これはXTestにどのように影響しますか?







さて、より多くのテストがあり、Xとのインターフェースはよりきれいで、

より抽象的な。

右、最初の部分をもう一度繰り返します。







より多くのテストがありますか?

はい、各テストは非常に具体的で、小さな仕様です

特定の動作。 そして、合計で彼らは与えるでしょう...







X APIの動作に関する完全な要件。

まさに! 開発が進むにつれて、テストスイートは

仕様-テストはますます具体的、具体的になり始めています。







もちろんわかります。

しかし、X APIの背後に隠されたクラスはどうなりますか? すべてが良いものになる理由

増加する要件のリストに対処するデザイナーですか?







豊富な要件に対処するには、当然、一般化する必要があります。

そう! 個々のケースごとにコードを書く代わりに、

それを一般化する。







そして、これは行動の接続性にどのように影響しますか?

開発プロセス中に、テストの動作はより明確になり、

コードはより一般的になりつつありますが、反対に動きます

一般性の軸に沿った方向。







また、接続性が低下しますか?

はい、コードがテストで説明されている要件を満たしている場合、

彼はまた、書かれていない要件をカバーする能力も持っています。 (3)







そして、これは非常に重要なポイントです。 一連のテストを考慮することができないため

すべての動作。 コード非常に汎用的であるため、カバレッジを通じて

多くのテストがシステムのすべての要件を満たし始めています。







テストが不完全だということですか?

もちろん! すべてを完全に記述することは単に非現実的です。 それでどうなりますか

コードの一般性を徐々に増やしていきますが、可能な限り

テストに合格し始めませんか?







わあ! 増加する失敗テストを書き続けます

失敗したテストを書くことができるまで、コードの共通性。

わあ!

行くぞ それでも埋め戻し-一般化のプロセスは解き放つプロセスであり、

一般化を解きます!







信じられない! つまり、構造と動作の両方を解き放ちます。

そうです、要点を言い直せますか?







さて、テストの構造はコードの構造を反映すべきではありません。

このような凝集により、システムが脆弱になり、リファクタリングが困難になります。

言い換えれば、テストの設計は一貫性を避けるためにコードに依存すべきではありません

彼と。

OK、動作はどうですか?







テストがますます具体的になるにつれて、コードはますます増えます。

共通。 テストとコードの動作は、逆方向に移動します

新しい失敗テストを書くことができなくなるまで、コミュニティの軸。

素晴らしい、すべてを理解していると思います!







反変テストで勝利を収めましょう!





(1)ここで、ボブおじさんは少し滑りました。 最も可能性が高いのは、テスト/コードを記述する段階の1つ-パブリックインターフェイスの形成についてです。 経験に応じて、開発者はインターフェースがどうあるべきかをすぐに知ることができます(頭の中で設計する)か、徐々にテストとコードを正しい方向に変えていきます。 この段階では、編集は両側で重要になります。 初心者は、優れたインターフェイスの作成方法がわからず、コードを2倍書き換える必要があるため、TDDを終了します。 しかし、これは素晴らしいプラクティスです! 横に移動してノートブックで座って、描き、考えます。







(2)決して行動のガイドではありません! これは単なる例です。 これに基づいてすべてのクラスを分割できるわけではありません。







(3)非常に物議をかもしているように思えるかもしれませんが、ボブおじさんは、コードを壊すようなテストや要件を思い付くことができないことを示唆しています。 コードはすでに非常に一般的であるため、説明できない多くの詳細を考慮します。








All Articles