TDDを使用して簡単な機能をコーディングするにはどうすればよいですか?


9

基本的にTDDの要点があります。私はそれが便利で、MSTESTフレームワークの適切なコマンドを持っていることを売りました。しかし、これまでのところ、これを主要な開発方法として使用するように卒業することはできませんでした。ほとんどの場合、コンソールアプリをテストドライバーとして書くための代理として使用します(私の従来のアプローチ)。

私にとって最も便利なのは、回帰テストの役割を吸収する方法です。

さまざまなテスト可能な動作を明確に分離するものはまだ何も構築していません。これは、私が知っている画像のもう1つの大きな部分です。

したがって、この質問は、次の開発タスクのために最初に何を書くかについてのポインタを尋ねることです。プロデューサ/コンシューマの方法でタスクの実行をカプセル化するコードを作成したいと思います。

このコードを書いた後、私は立ち止まり、この質問を書くことにしました(今回、実際にTDDを実際に使用できるかどうか疑問に思いました)

コード:

interface ITask
{
    Guid TaskId { get; }
    bool IsComplete { get; }
    bool IsFailed { get; }
    bool IsRunning { get; }
}

interface ITaskContainer
{
    Guid AddTask(ICommand action);
}

interface ICommand
{
    string CommandName { get; }
    Dictionary<string, object> Parameters { get; }
    void Execute();
}

最初にテストを記述し、次にインターフェースを記述しておく必要があります。全体のアイデアは、TDDがAPIのためのものであるということです。

1
まだ存在しないインターフェースのテストをどのように記述しますか?それらはコンパイルさえしません。
ロバートハーヴェイ

5
これが最初の失敗したテストです。
cori

回答:


10

この概念から始めます
。1)希望する動作から始めます。そのためのテストを書きます。テスト失敗を参照してください。
2)テストに合格するのに十分なコードを記述します。すべてのテストに合格することを確認します。
3)冗長な/ずさんなコードを探す->リファクタリング。テストがまだ成功することを確認してください。後藤1

したがって、#1で、新しいコマンドを作成したいとしましょう(コマンドの機能を拡張しているので、我慢してください)。(また、私は極端なTDDではなく少し実用的です)

新しいコマンドはMakeMyLunchと呼ばれるため、最初にそれをインスタンス化するためのテストを作成し、コマンド名を取得します。

@Test
public void instantiateMakeMyLunch() {
   ICommand command = new MakeMyLunchCommand();
   assertEquals("makeMyLunch",command.getCommandName());
}

これは失敗し、新しいコマンドクラスを作成して、その名前を返すように強制します(純粋に言うと、これはTDDの1ラウンドではなく2ラウンドです)。したがって、クラスを作成し、コマンド名を返すなど、ICommandインターフェイスを実装するようにします。すべてのテストを実行すると、すべてのパスが表示されるので、リファクタリングの機会を探すために進みます。おそらくない。

したがって、次に実行を実装する必要があります。「MakeMyLunch」が「ランチを作った」ことをどのようにして知ることができるでしょうか。この操作により、システムはどのように変化しますか?これをテストできますか?

次のことをテストするのが簡単だとします。

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   ICommand command = new MakeMyLunchCommand();
   command.execute();
   assertTrue( Lunch.isReady() );
}

他の場合には、これはより難しく、あなたが本当にやりたいことは、テスト対象(MakeMyLunchCommand)の責任をテストすることです。おそらく、MakeMyLunchCommandの責任は、冷蔵庫と電子レンジを操作することです。したがって、それをテストするには、模擬冷蔵庫と模擬電子レンジを使用できます。[2つのサンプルモックフレームワークはMockitonMock です。または、こちらをご覧ください。]

その場合は、次の疑似コードのようにします。

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   Fridge mockFridge = mock(Fridge);
   Microwave mockMicrowave = mock(Microwave);
   ICommand command = new MakeMyLunchCommand( mockFridge, mockMicrowave );
   command.execute();
   mockFramework.assertCalled( mockFridge.removeFood );
   mockFramework.assertCalled( microwave.turnon );
}

純粋主義者は、クラスの責任をテストします-他のクラスとの相互作用(コマンドが冷蔵庫を開いて電子レンジをオンにしましたか?)。

実用主義者は、クラスのグループのテストと結果のテスト(ランチの準備はできていますか?)と言います。

システムに適したバランスを見つけてください。

(注意:インターフェイス構造にたどり着いたのが早すぎると考えてください。ユニットテストと実装を作成するときに、おそらくこれを進化させて、ステップ3で共通のインターフェイスの機会を「通知」することができます)。


インターフェースを事前に記述していなかった場合、どの質問がExecute()メソッドの作成につながったでしょうか-追加機能を刺激するための「ステップ」がないとTDDへの最初の試みの一部が停止しました-私は得ます
避け

1
良い質問!作成したコマンドが「MakeMyLunchCommand」だけだった場合、メソッドは「.makeMyLunch()」で始まっている可能性があります。それは大丈夫だったでしょう。次に、別のコマンドを作成します( "NapCommand.takeNap()")。まだ別の方法で問題はありません。次に、エコシステムでそれを使用し始めます。これは、ICommandインターフェースに一般化することを余儀なくされる場所です。一般的に、YAGNI [ en.wikipedia.org/wiki/You_ain't_gonna_need_it ] =)それ以外の場合は、そこに着くのでそれから始めるので、一般化を最後の責任ある瞬間まで遅らせることがよくあります。
jayraynet 2012

(ここでは、メソッド名などのリファクタリングを簡単にする最新のIDEを使用していることも前提としています)
jayraynet

1
アドバイスに再度感謝します。最終的にすべての部品とそれらがどのように適合するかを確認することは私にとって一種のマイルストーンです-そして、はい、クイックリファクタリングは私のツールボックスにあります
アーロンアノディ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.