Specflowを使用してBDD(Behavior Driven Development)のテストケースを書くことを学んでいます。 BDDで包括的なテストを作成する場合、TDD(テスト駆動開発)テストを個別に作成する必要がありますか? TDDとBDDの両方のテストケースを別々に記述する必要がありますか、それとも実質的に同じものですか?
どちらも同じであるように見えますが、唯一の違いは、BDDテストケースは非開発者とテスターが理解できることです。
BDDとTDDの違いは、BDDはBで始まり、TDDはTで始まるということです。しかし、真剣に、TDDの問題は、ユニットテストを書くときに「方法」に焦点を合わせている開発者が多すぎて、システムが機能することを確認する以外に何もしなかった脆弱なテスト。
BDDは新しい語彙を提供するため、単体テストの作成に重点を置いています。基本的に、これはTDDに対する機能主導のアプローチです。
Behavior Driven Developmentは、Test Driven Developmentの拡張/改訂版です。その目的は、システムを考案している人々(つまり、開発者)が適切なテスト(つまり、利害関係者が望む動作を反映するテスト)を特定するのを助けることです。結果は同じです-テストを開発してから、テストに合格するコード/システムを開発します。 BDDの希望は、テストがシステムが要件を満たしていることを示すのに実際に役立つことです。
[〜#〜]更新[〜#〜]
コードの単位(個々のメソッド)は、動作テストによって表される動作を表現するには細かすぎる可能性がありますが、それらが適切に機能することを保証するために、ユニットテストでテストする必要があります。これが「TDD」テストの意味するところであれば、はい、まだ必要です。
BDDは、「ユビキタス言語」と呼ばれるものを利用します。これは、開発者と顧客の両方が理解できる一連の知識です。このユビキタス言語は、顧客の理解のレベルで、必要な要件とテストを形成および開発するために使用されます。
BDDが要求する要件とテストの範囲内で、「通常の」TDDを使用してソフトウェアを開発します。このように作成された単体テストは実装コードのテストスイートとして機能し、BDDテストは多かれ少なかれ顧客の受け入れテストとして機能します。
私の経験では、TDDの最大の問題は "[〜#〜] t [〜#〜]"です。これにより、素人(マネージャー、テスター、非TDD開発者)は、それをウォーターフォールスタイルの従来の開発後の「テスト」フェーズと同一視します。それは誰でも頭を動かすことができるものです。
多くの人が直面している問題は、TDDがテスターではなく開発者向けであることです。正しく行われるTDDは、主にテスト戦略や受け入れテストツールではなく、優れたソフトウェア設計をゼロから推進する手法です。小さな疎結合クラス、明確で明確に定義されたインターフェース、継続的なリファクタリングを通じてコードを継続的にクリーンアップします。日常的に、頻繁に、自信を持って実行されるリファクタリング。
CI /ビルドプロセスのpartを形成できる包括的なテストスイートが偶然得られることは、目標ではなくボーナスです。
BDDは、ビジネス要件とより高いレベルの受け入れテストの間のギャップを埋めることにより、これを補完します。開発プロセスの範囲を限定し、製品全体がいつ適切に提供されたかを決定するのは、BDDスイートの満足度です。
TDDとBDDの違いは微妙で、主にlanguageに要約されます。 BDDテスト は、多くの場合、次の形式で記述されます。
public void shouldBuyBread() throws Exception {
//given
given(seller.askForBread()).willReturn(new Bread());
//when
Goods goods = shop.buyBread();
//then
assertThat(goods, containBread());
}
behaviorの観点からテストを構成すると、クラスの責任範囲が限定され、設計が改善されます(少なくともBDD'ersによると)。 BDDは、ドメインの専門家/顧客が理解できる実行可能な仕様に焦点を当てることがあります。
BDDは、状態ベースの検証とは対照的に、 Martin Fowlerが「アウトサイドイン」または「モックテスト」テストと呼んでいる とより関連しています。
私の最後の返事はあまりうまくいかなかったので、私は非常に簡単なアプローチを試みます。
Behaviour Driven Development
はTest Driven Development
のサブセットですTDD
は、すべての関数のすべての単体テストに焦点を当てており、何をするかは関係ありません。 BDD
は重要なソフトウェアに焦点を当てていますIdiom
。 TDD
はテストを解決し、BDD
は story telling 形式を強制しますJavaScriptの例
jasmine (BDD
)の単体テスト
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
JsUnity(TDD
)での単体テスト
function test_lists() { assert.areEqual([1, 2, 3], [1, 2, 3]) }
UnittestフレームワークでBDD
のようなテストを作成するのに役立ついくつかのPythonライブラリを次に示します。
BDDは、テストに1レベルの抽象化を追加します。上位レベルのコード(通常はtxt)はシステムがテストする内容を示し、下位レベルのコードはシステムがテストする方法を示します。したがって、BDDフレームワークは、下位レベルのコードでTDDフレームワークを使用できます。
これは、乾燥した状態を維持することで大きな助けになります。 TDDを使用すると、コードの重複を多く含む「ウェット」テストが簡単に発生する可能性があります。これにより、コードとそれを使用したテストをリファクタリングすることで、テストを簡単に破ることができます。 BDDでは、コードをリファクタリングして下位の抽象化レベルのみを変更する必要があるため、仕様が変更されない場合でも、上位レベルのコードは変更されません。
ところでこれは単純なClean Codeです。通常、抽象化レベルの高いものを読んでそれが何であるかを理解するのに十分であり、本当に必要な場合にのみ、抽象化レベルの低いテストコードを掘り下げます。これにより、(テスト)コードが一般的に理解しやすくなります。
悪い例(単純すぎるため):
ジャスミンTDDスタイル
calculator.add.specs
_describe("Calculator: add", function (){
it("should be able to add 2 numbers together", function (){
var total = add(1, 2);
expect(total).toBe(3);
});
it("should be able to add 3 numbers together", function (){
var total = add(1, 2, 3);
expect(total).toBe(6);
});
});
_
jasmine-cucumber BDDスタイル
電卓の仕様
_feature('Calculator: add')
.scenario('should be able to add 2 numbers together')
.when('I enter "1"')
.and('I add "2"')
.then('I should get "3"')
.scenario('should be able to add 3 numbers together')
.when('I enter "1"')
.and('I add "2"')
.and('I add "3"')
.then('I should get "6"')
_
電卓のステップ
_featureSteps('Calculator:')
.before(function(){
this.values = [];
this.total = null;
})
.when('I enter "(.*)"', function(value){
this.values.Push(Number(value));
})
.when('I add "(.*)"', function(value){
this.values.Push(Number(value));
})
.then('I should get "(.*)"', function(expectedTotal){
this.total = add.apply(null, this.values);
expect(this.total).toBe(Number(expectedTotal));
});
_
実装
calculator.js
_function add(){
var args = Array.prototype.slice.call(arguments);
var total = 0;
for (var i in args)
total += args[i];
return total;
}
_
add()
関数の名前をsum()
に変更すると、TDDコードを2か所変更する必要がありますが、BDDコードはstepsファイル。 Ofc。抽象化レベルをもう1つ追加するには、より多くのコードが必要です...
混乱を避けるために、多くの人がBDDをキュウリと関連付けていることを理解してください。 Gherkin派生の構文を使用して記述したテストはBDDチェックと見なされ、ユニットテストフレームワーク(junit、unittest.pyなど)を使用して記述されたすべてのテストはTDDと見なされます。
それは間違っています。この場合、媒体はメッセージを定義しません。モック(IMHO)の使用も行いません。
主な違いはすでに説明されています。BDDは、機能主導の手法であり、ユビキタス言語と外部からのアプローチを使用しています。ストーリーまたはストーリーのスライスの仕様(後でテストとして実行されます)を記述します。小ささと具体性が重要であり、それは物語のこの側面から必要とされる/期待されるものの「例による説明」です。
BDD仕様は、機能を指定する必要のある人々がすべてコードを読み書きできない場合、ガーキンに似た言語で頻繁に記述されます(大規模な組織や実際の顧客がチームの一部である場合など)。それ以外の場合、gherkin-eseは必要ありません。これらは、有名な3つのAmigosミーティングの成果である傾向があります。 BDDフレームワーク(フィット、キュウリなど)を使用すると、テストが遅くなり、インフラストラクチャが少し必要になるため、これらを比較的疎に保ちます(ストーリーごとに数個)。
TDDはコードの作成時に使用されます。目的は、リファクタリングの機会がたくさんあり、新しいコードの記述中に誤って何かを壊していないという合理的な自信を持って、機能の記述を規則正しく進めることです。システムの機能を説明する必要はなく、メソッドの機能(または共通の効果を持ついくつかの機能)のみを説明します。開発者以外はここで書かれたテストを読む必要はありませんが、彼らは驚くほど速く実行してエラーを分離する必要があります。クラスのメンバー関数(「メソッド」)ごとに多くのテストが存在する可能性があるため、非常に速く実行されます。
外層でBDDを実行する場合でも、リファクタリングとエラーをより迅速に特定して回避できるため、内ループでTDDを実行すると便利です。
どちらも、大規模システムの正当性を証明するものではありません。どちらもペンとパフォーマンスのテストに代わるものではありません。どちらもスレッドの安全性を保証しません。テストは開発を支援するものであり、完全なバグ耐性を保証するものではありません。