私はTDDにかなり慣れていないので、実装コードの前に来る最初のテストを作成するときに問題があります。実装コードにフレームワークがなければ、私は最初のテストを自由に書くことができますが、常にJava/OO way問題について考えることの。
たとえば、私の Github ConwaysGameOfLifeExample で最初に書いたテスト(rule1_zeroNeighbours)では、まだ実装されていないGameOfLifeオブジェクトを作成することから始めました。存在しないsetメソッド、存在しないstepメソッド、存在しないgetメソッドを呼び出し、アサートを使用しました。
テストを書いてリファクタリングすると、テストは進化しましたが、元々は次のようなものでした。
@Test
public void rule1_zeroNeighbours()
{
GameOfLife gameOfLife = new GameOfLife();
gameOfLife.set(1, 1, true);
gameOfLife.step();
assertEquals(false, gameOfLife.get(1, 1));
}
これは、この最初の段階でこの最初のテストを作成することに決めた方法に基づいて、実装の設計を強制しているときに奇妙に感じられました。
TDDを理解する方法でこれは大丈夫ですか?私はTDD/XPの原則に従っているようですが、テストと実装はリファクタリングによって時間とともに進化しました。そのため、この初期の設計が役に立たないことが判明した場合、変更する可能性がありましたが、このようにして、ソリューションの方向性を強制しているような気がします。
他にどのようにTDDを使用しますか? GameOfLifeオブジェクトではなく、プリミティブと静的メソッドのみで開始することで、リファクタリングをさらに繰り返すことができたかもしれませんが、それはあまりにも不自然に思われます。
これは、この最初の段階でこの最初のテストを作成することに決めた方法に基づいて、実装の設計を強制しているときに奇妙に感じられました。
これがあなたの質問の要点だと思います。これが望ましいかどうかは、事前に設計してからTDDを使用して実装を埋める必要があるというコデニンジャの考え方に頼るか、またはテストに関与する必要があるというダロンの考え方に依存します設計と実装を推進する。
どちらを好むか(または真ん中に落ちる場所)は、好みとして自分で発見する必要があるものだと思います。各アプローチの長所と短所を理解しておくと役立ちます。おそらくたくさんありますが、主なものは次のとおりです。
Pro Upfront Design
プロテスト-運転設計
コードのクライアント(テスト)を中心に実装を構築することで、不要なテストケースの作成を開始しない限り、ほとんど無料でYAGNIに準拠します。より一般的には、消費者による使用を中心に設計されたAPIを取得します。これは、最終的に必要なものです。
コードを記述する前に一連のUMLダイアグラムを描画し、ギャップを埋めるというアイデアはいいですが、現実的であることはまれです。 Steve McConnellのコードコンプリートでは、デザインは「邪悪な問題」として有名に説明されています。これは、最初に少なくとも部分的に解決しないと完全に理解できない問題です。これと、根本的な問題自体が要件の変化によって変化する可能性があるという事実を組み合わせると、この設計モデルは少し絶望的に感じ始めます。テスト走行では、実装だけでなく、設計時に一度に1つの作業の塊を噛み切ることができ、少なくとも赤から緑に変わるまでの期間、そのタスクは最新であり、適切であることがわかります。
特定の例については、durronが言うように、可能な限り最小限のインターフェイスを使用して、最も単純なテストを記述してデザインを駆り立てるアプローチを採用した場合、おそらくコードスニペットのインターフェイスよりもシンプルなインターフェイスから始めます。 。
そもそもテストを作成するには、実装するAPI(-===-)を設計する必要があります。 entireGameOfLife
オブジェクトを作成するためのテストを記述し、それを使用してテストを実装することで、すでに間違った足で始めています。
JUnitとMockitoを使用した実用的なユニットテストから :
最初は、そこにさえない何かを書くのにぎこちなく感じるかもしれません。コーディングの習慣を少し変更する必要がありますが、しばらくすると、それが優れた設計の機会であることがわかります。最初にテストを記述することにより、クライアントが使用するのに便利なAPIを作成する機会があります。テストは、新しく生まれたAPIの最初のクライアントです。これこそが、TDDが本当に重要なこと、つまりAPIの設計です。
テストでは、APIを設計する試みはあまり行われません。すべての機能が外部GameOfLife
クラスに含まれるステートフルシステムをセットアップしました。
このアプリケーションを作成する場合、代わりに、自分が構築したい部分について考えます。たとえば、大きなアプリケーションに移る前に、Cell
クラスを作成し、そのテストを記述します。私は確かに、Conwayを適切に実装するために必要な「あらゆる方向に無限」のデータ構造のクラスを作成し、それをテストします。それがすべて終わったら、main
メソッドなどを含むクラス全体を書くことを考えます。
「失敗するテストを作成する」ステップは簡単に理解できます。しかし、希望どおりに機能する失敗したテストを書くことはTDDの中核です。
これについては別の考え方の学校があります。
一部の人は言う:コンパイルしないテストはエラーです-修正して、利用可能な最小の量産コードを書いてください。
いくつか言う:最初にテストを記述して、それがアリを吸う(またはしない)かどうかをチェックしてから、不足しているクラス/メソッドを作成することは問題ありません
最初のアプローチでは、本当に赤緑のリファクタリングサイクルにあります。もう1つは、達成したいことについて少し広い概要です。
どちらの方法で作業するかは、あなた次第です。私見両方のアプローチが有効です。
「一緒にハックする」方法で何かを実装する場合でも、プログラム全体に関与するクラスとステップを考えます。あなたはこれを熟考し、最初にこれらのデザインの考えをテストとして書き留めました-それは素晴らしいです!
次に、両方の実装を繰り返してこの初期テストを実行し、さらにテストを追加して設計を改善および拡張します。
あなたに役立つかもしれないものは、テストを書くために Cucumberまたは類似のもの を使用することです。
JavaまたはC#で記述されたsystemレベルのテストがその理由で好きではありません。c#またはCucumberベースのテストフレームワークのSpecFlowを見てください) Java(たぶんJBehave)の場合。テストは次のようになります。
また、すべてのシステムテストを変更することなく、オブジェクトの設計を変更できます。
(「通常の」単体テストは、単一のクラスをテストする場合に最適です。)