簡単なコードのテスト

コードが簡単であっても、テストする必要があります。

数日前に、ロバート・マーティンは「TDDのプラグマティズム」と呼ばれる投稿( ここでは翻訳 - 翻訳者の コメント )を公開し、 すべてのコードを完全にテストしたわけではないと述べました。 TDDを使用する価値のない例外的な状況の中で、ボブおじさんはGUIコードの記述に言及しており、そのようなステートメントのポイントを参照していますが、例外として、私の意見では、 非論理的です。

ロバート・マーティンは、彼はテストを通して開発していないと主張しています。



基本的に、これらのステートメントは、テストを通じてゲッターやセッター(.NETプログラマーのプロパティ)などの「簡単な」コードを開発しない唯一のルールに要約されます。

このTDDルールに関連するいくつかの問題について説明します。



これらの各ポイントをさらに詳しく調べてみましょう。



因果関係



TDDの全体的なポイントは、テスト実装をガイドすることです。 テストは原因であり、実装は結果です。 この原則に従えば、予想される実装は非常に複雑になるため、地球上でテストを作成しないことをどのように決定できますか? あなたはまだこれさえ知りません。 これは論理的に不可能です。

Robert Martin自身 Transformation Priority Premise(TPP) 条件を提案しましが、考えの1つは、テストを通じて新しいコードの開発のガイドを開始するときに、小さな正式な手順でこれを行う必要があるということです。 最初のステップは、定数を返すなどの簡単な実装につながる可能性があります。

TPPの観点から見ると、自明なコードと自明でないコードの唯一の違いは、テストを通じて実装を指示するプロセスをどこまで進めるかです。 したがって、必要なのが「自明」である場合、唯一の正しい方法は、自明な動作が期待どおりに機能することを実証する単一のテストを書くことです。 したがって、TPPによると、あなたは仕事をします。



カプセル化。



ゲッターとセッターに対するロバート・マーティンの例外は特に困惑しています。 これまたはそのゲッター/セッター(または.NETプロパティ)が些細なものだと思うのなら、なぜそれを実装したのでしょうか? なぜクラスにオープンフィールドを実装しないのですか?

クラス内のパブリックフィールドの実装を回避することには優れた理由があり、 それらはすべてカプセル化に関連しています。 データはゲッターとセッターを使用して実装する必要があります。これにより、将来的に実装を変更できるようになります。

動作を変更せずに実装を変更するプロセスを正確に何と呼びますか?

これをリファクタリングと呼びます。 実装コードを変更しても動作が変わらないことをどのように知ることができますか?

「リファクタリング」という本の Martin Fowler よると、セーフティネットとして適切なテストセットを用意する必要があります。そうしないと、何かを壊したかどうかがわかりません。

優れたコードは長い間存続し、開発されています。 当初は些細なことでしたが、長期間にわたって変化する可能性があり、些細な用語が数年間些細なままであるかどうかを予測することはできません。 複雑さを追加しても些細な動作が正しいままであることを確認することが重要です。 リグレッションテストスイートは、この問題を解決するように設計されていますが、これは実際に些細な機能のテストを作成する場合に限ります。

ロバート・マーティンは、ゲッターとセッターが他のテストを介して間接的にテストされると主張しますが、これはメンバーを宣言する段階では真実かもしれませんが、これが任意の長い間真実であるという事実ではありません。 数か月後、これらのテストは削除され、入力された些細なメンバーはテストでカバーされません。

このように見ることができます。TDDを使用してTPPをフォローするかどうかはできますが、通常のメンバーの場合、最初のコンバージョンと2番目のコンバージョンの時間差は分ではなく月で測定できます。



TDDの学習



プラグマティストであるのは良いことだと思いますが、テストを通じて「些細な」コードを開発する必要がないという「ルール」は、初心者にとってひどいアドバイスです 。 TDDを勉強している人に、このTDD自体を回避するために使用できるパスを与えると、この人は、困難に直面して、毎回そのルートに行きます。 既にこのような回避策を提供している場合は、少なくとも条件を明示的かつ測定可能にする必要があります。

「実装が簡単になると予測できる」というあいまいな状態は、まったく測定されていません。 特定の実装がどのように見えるかを知っていると思うかもしれませんが、テストを通じて実装を指示できるようになると、しばしば驚かれることでしょう。 TDDが私たちを導くのは、このような考え方です-あなたが最初に機能すると思っていたことが機能しません。



根本原因分析



すべてのgetterおよびsetterにTDDを適用する必要があると主張しますか? はい、私は本当に主張します。

そのようなアプローチには時間がかかりすぎると言えます。 ロバート・マーティンは皮肉なことにこれについて次のように述べています。
「速く動くための唯一の方法は、有能に動くことです。」


それでも、TPPをプロパティに適用するとどうなるかを見てみましょう(Javaプログラマは読み続けることができます。C#のプロパティは、getterとsetterの単なる構文上の砂糖です)。

DateViewModelクラスがあり、年を表すint型プロパティが必要だとしましょう。 最初のテストは次のようになります。

[Fact] public void GetYearReturnsAssignedValue() { var sut = new DateViewModel(); sut.Year = 2013; Assert.Equal(2013, sut.Year); }
      
      





当たり前のことを考えたり、すべてを疑ったりすることなく、以下が正しい実装になります。

 public int Year { get { return 2013; } set { } }
      
      







TPPの後、このコードはまさにそのようになります。 さて、別のテストを書いてみましょう:

 [Fact] public void GetYearReturnsAssignedValue2() { var sut = new DateViewModel(); sut.Year = 2010; Assert.Equal(2010, sut.Year); }
      
      





これらの2つのテストを組み合わせることで、プロパティを正しく実装することができます。

 public int Year { get; set; }
      
      





これら2つのテストは1つのパラメーター化されたテストにリファクタリングできるという事実にもかかわらず、まだ多くの作業が行われています。 1つのプロパティに対して2つのテストを記述する必要があるだけでなく、それぞれに対してもテストを行う必要があります。

「まったく同じことをしますか?」とあなたは尋ねます。 あなたは本当に何ですか?

ああ、あなたはプログラマーです。 プログラマーは、同じことを何度もしなければならないときに何をしますか?

まあ、あなたがあなたを傷つける仕事を避けることを可能にする矛盾するルールを我慢するなら、これは間違った答えです。 仕事があなたを傷つけたら-より頻繁にそれをしなさい。

プログラマーは反復アクションを自動化します。 プロパティテストでも同じことができます。 AutoFixtureを使用して同じことを行った方法は次のとおりです。

 [Theory, AutoWebData] public void YearIsWritable(WritablePropertyAssertion a) { a.Verify(Reflect<DateViewModel>.GetProperty<int>(sut => sut.Year)); }
      
      





これは、前の2つのテストと同じ動作をテストする宣言的な方法ですが、1行の長さです。

ここでは根本原因分析が適切です。 TDDのゲッターおよびセッターへの適用に関するコスト/利益比は非常に高いという印象を受けます。 しかし、私はロバート・マーティンがこれに留まっていたと思う。なぜなら、彼はコストが固定され、利益が小さすぎると考えたからだ。 ただし、メリットは小さいように見えるかもしれませんが、コストを高く固定する必要はありません。 低コストとコスト/利益の比率が向上します。 そのため、テストを通じてゲッターとセッターの開発を常にリードする必要があります。



翻訳者から:次回AutoFixtureレビューの翻訳をリリースするか、自分でレビューする予定です。 私見、非常に興味深いツール。



All Articles