TDD方法論をトップダウンで適用できますか?


13

方法論であるTDDが次のケースをどのように処理するかはわかりません。Pythonでmergesortアルゴリズムを実装するとします。私は書くことから始めます

assert mergesort([]) === []

そしてテストは失敗します

NameError:名前 'mergesort'は定義されていません

次に追加します

def mergesort(a):
    return []

テストに合格しました。次に追加します

assert mergesort[5] == 5

そして私のテストは失敗します

AssertionError

私はパスします

def mergesort(a):
    if not a:
        return []
    else:
        return a

次に、追加します

assert mergesort([10, 30, 20]) == [10, 20, 30]

そして今、私はこの合格を試みなければなりません。mergesortアルゴリズムを「知っている」ので、次のように記述します。

def mergesort(a):
    if not a:
        return []
    else:
        left, right = a[:len(a)//2], a[len(a)//2:]
        return merge(mergesort(left)), mergesort(right))

そして、これは失敗します

NameError:名前 'merge'は定義されていません

ここで質問です。どうすればmergeTDDを使用して実装を開始できますか?この「ハング」が満たされていないためmergesort、テストが失敗し、終了するまで合格しmergeないため、私はできないようです!このテストがぶらぶらしていると、TDDの反復構築中に「グリーン」にならないため、TDDを実際に実行することはできませんmerge

私は次の3つのいシナリオに固執しているようで、(1)TDDコミュニティがこれらのどれを好むか、(2)私が見逃している別のアプローチがありますか?ボブおじさんのTDDのチュートリアルを何度か見たことがありますが、以前にこのようなケースを見たことを覚えていません!

以下に3つのケースを示します。

  1. 別のテストスイートを使用して、別のディレクトリにマージを実装します。
  2. ヘルパー関数を開発する際に環境に配慮する必要はありませ実際に合格したいテストを手動で追跡するだけです。
  3. コメントアウト(GASP!)またはmergesortその呼び出しの行を削除しますmergemerge仕事を始めたら、元に戻します。

これらはすべて私には馬鹿げているように見えます(または、私はこれを間違って見ていますか?)。誰もが好ましいアプローチを知っていますか?


2
TDDの目標の一部は、ソフトウェア設計の作成を支援することです。その設計プロセスの一部は、望ましい結果を生み出すために必要なものを発見することです。の場合、これはmergesortすでに非常に明確に定義されたアルゴリズムであるため、この発見プロセスは不要であり、設計であることがすでにわかっているものを一連の単体テストにマッピングする問題になります。おそらく、あなたのトップレベルのテストでは、テスト中のあなたの方法でソートされていないコレクションを受け入れて... 1ソート返すことを主張
ロバート・ハーヴェイ

1
...その後の単体テストでは、Aの実際の仕組みを徐々に掘り下げていきmergesortます。これを行うための「正しい」方法を探している場合、mergesortアルゴリズムの一連の単体テストへのマッピングについて正確にする以外に、それはありません。すなわち、彼らはmergesort実際に何をすべきかを反映すべきです。
ロバートハーヴェイ

4
設計は単体テストだけでは成長しません。mergesortデザインがred-green-refactorから自然に出現することを期待している場合、既存の知識に基づいてプロセスをガイドしない限り、それは起こりませんmergesort
ロバートハーヴェイ


1
TDD mergeでは、「リファクタリング」段階でのみ発明する必要があります。mergeテストに合格するためにメソッドを導入できる場合は、mergesort最初にmergeメソッドなしでテストに合格させます。次に、mergeメソッドを導入して実装をリファクタリングします。
ファビオ

回答:


13

以下に、オプションを確認するいくつかの代替方法を示します。しかし、最初に、私が強調しているボブおじさんからのTDDのルール:

  1. 失敗した単体テストに合格しない限り、実稼働コードを作成することはできません。
  2. 失敗するのに十分な数以上の単体テストを作成することはできません。コンパイルの失敗は失敗です。
  3. 1つの失敗した単体テストに合格するのに十分な量を超える本番コードを記述することはできません。

したがって、ルール番号3を読み取る1つの方法はmerge、テストに合格するために関数が必要なため、それを実装できることですが、最も基本的な形式でのみです。

または、代わりに、マージ操作をインラインで記述することから始めて、テストを機能させた後、リファクタリングして関数に戻します。

別の解釈は、mergesortを書いているということです。merge操作が必要であることはわかっています(つまり、YAGNIではなく、「十分な」ルールが削減しようとするものです)。したがって、マージのテストを開始してから、全体のソートのテストに進む必要がありました。


これらは本当に良い観察です。私は以前にインラインとファクタリングを考えていましたmergeが、驚くほど面倒で、エッジケースごとに(そしてスタンドアロンとしても有用です)独立した関数としてそれを行うという考えはより理にかなっています。しかし、基本的な形でインラインで実行し、ブルーハットの段階でそれをファクタリングするスタイルは、本当に正しいようで、私が探していたものです。
レイトーアル

@RayToal-私は実際にmerge、ソートを実行する前に操作を完全にテストするアプローチ(およびpartition操作の個別のテスト)に傾いています。主張されている利点の創発的デザインは、既知の目標に向かってゆっくりと取り組むことから来ると思います。マージソートの場合、目標は一般的なソートだとは思いません(そうするとバブルソートになってしまいます)。基本的な操作を知っているので、それらの操作に向けて働きます。並べ替えはほとんどの場合、後付けです。
kdgregory

1
4番目のオプションがあります。merge関数を渡し、mergesortその動作をモックします。次に戻って、merge最初にテストを実装します。デリゲートは素晴らしい™です。
ラバーダック

@RubberDuckコアドメインの不可欠な部分をモックすると、いくつかの問題が発生する可能性があります。具体的には、プログラムを実行し、モック機能とマージ機能の詳細が異なる場合です。そのようなアプローチは、ソートするリストがどこから来たかなどの外部リソースを使用している場合に残すべきです。
cllamach
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.