2009-07-24 17 views
4

私はこれまで、さまざまな理由でユニットテストを書いたことがありません。私は一から作成する小さなアプリを持っているので、今、快適にテストを書く機会があります。この状況でユニットテストをどのように適用しますか?

しかし、私は少し困惑しています。アプリケーションは、スマートカードリーダーを備えたプリンターを使用してスマートカード上のデータをプログラムすることになっています。ここでは、デバイスコンテキストの作成、プリンタモードの設定、ドキュメントの初期化、プリンタへのカードのフィード、リーダによるカードへの接続、カードへの何かの書き込み、カードの取り出し、ドキュメントの終了、デバイスコンテキスト。

さて、単体テストは各テストごとに1つの関数をテストし、各テストは他のテストの結果とは独立して実行されるはずです。しかし、見てみましょう - 私はそれがプリンタに正しく配置されていない場合、私はそれに接続していない場合、スマートカードへの書き込みをテストすることはできません。実際のカードが正しく配置されて接続されていれば、実際に書き込みが行われたかどうかをテストすることしかできません。カードへの接続が失敗すると、カードへの書き込みをテストする方法がないので、テストの独立性の原則は破られます。

は、これまでのところ私はこのようなテストを思い付いた

[Test] 
public void _WriteToSmartCard() 
{ 
//start print job 
printer = new DataCardPrinter(); 
reader = new SCMSmartCardReader(); 
di = DataCardPrinter.InitializeDI(); 
printer.CreateHDC(); 
Assert.AreNotEqual(printer.Hdc, 0, "Creating HDC Failed"); 
Assert.Greater(di.cbSize, 0); 

int res = ICE_API.SetInteractiveMode(printer.Hdc, true); 
Assert.Greater(res, 0, "Interactive Mode Failed"); 

res = ICE_API.StartDoc(printer.Hdc, ref di); 
Assert.Greater(res, 0, "Start Document Failed"); 

res = ICE_API.StartPage(printer.Hdc); 
Assert.Greater(res, 0, "Start Page Failed"); 

res = ICE_API.RotateCardSide(printer.Hdc, 1); 
Assert.Greater(res, 0, "RotateCardSide Failed"); 

res = ICE_API.FeedCard(printer.Hdc, ICE_API.ICE_SMARTCARD_FRONT + ICE_API.ICE_GRAPHICS_FRONT); 
Assert.Greater(res, 0, "FeedCard Failed"); 

bool bRes = reader.EstablishContext(); 
Assert.True(bRes, "EstablishContext Failed"); 

bRes = reader.ConnectToCard(); 
Assert.True(bRes, "Connect Failed"); 

bRes = reader.WriteToCard("123456"); 
Assert.True(bRes, "Write To Card Failed"); 

string read = reader.ReadFromCard(); 
Assert.AreEqual("123456", read, "Read From Card Failed"); 

bRes = reader.DisconnectFromCard(); 
Assert.True(bRes, "Disconnect Failde"); 

res = ICE_API.SmartCardContinue(printer.Hdc, ICE_API.ICE_SMART_CARD_GOOD); 
Assert.Greater(res, 0, "SmartCardContinue Failed"); 

res = ICE_API.EndPage(printer.Hdc); 
Assert.Greater(res, 0, "End Page Failed"); 

res = ICE_API.EndDoc(printer.Hdc); 
Assert.Greater(res, 0, "End Document Failed"); 
} 
テストが働いている

(「適切」であり、あまりにも他のものをテストし、他のテストもあります)が、原則は壊れている - それは複数の機能をテストし、多くの機能をテストします。それに続く各機能は、前の機能の結果に依存します。ここで、私たちは、このような状況で単体テストにどのようにアプローチすればよいのでしょうか。

+0

C#ラッパーにアクセスする可能性はありますか? VB6とC++のコードに頼っていますが、同じことをやっています。より良いIDEとフレームワークですべてをやり遂げることが大好きです。 – fuzz

答えて

4

テストコードは、しばしば統合テストと呼ばれます。要するに、統合テストは、システムのコンポーネント間の統合をチェックするテストとして定義されることがよくあります。 David Reisが述べるように、単体テストはしばしば個々のメソッドをテストします。

どちらのクラスのテストも便利です。あなたのような統合テストは、最初から最後までシステムを実行し、すべてがうまく連携していることを確認します。しかし、彼らは遅く、しばしば外部依存(カードリーダーのようなもの)があります。単位テストは、小さく、速く、焦点が合っていますが、単体テストであれば木の森を見るのは難しいです。

ユニットテストは、統合テストとは別のディレクトリに配置します。継続的な統合を使用する。統合テストを遅くし、より多くのセットアップ/展開が必要なため、1日に数回しか実行しないでください。常にユニットテストを実行してください。

ここで、メソッドが他のメソッドに依存する特定の状況をどのようにユニットテストしますか? あなたが制御するコードの量とライブラリにどれくらいの量があるのか​​は明らかではありませんが、コードでは可能な限り依存性注入(DI)を使用する方法を学びます。

boolean WriteToCard(String data){ 
    // do something to data here 
    return ICE_API.WriteToCard(ICE_API.SOME_FLAG, data) 
} 

さてあなたは、のようなものに変更することができるはず読者方法は、(擬似コードで)このようになりますと仮定します。でWriteToCardのために、あなたのテストに続いて

ICE_API api = null 

    ICE_API setApi(ICE_API api) { 
     this.api = api 
    } 

    ICE_API getApi() { 
     if (api == null) { 
     api = new ICE_API() 
     } 
    } 

    boolean WriteToCard(String data){ 
     // do something to data here  
     return getApi().WriteToCard(ICE_API.SOME_FLAG, data) 
    } 

あなたが行う設定

void setup() 
    _mockAPI = new Mock(ICE_API) 
    reader.setApi(_mockAPI) 

void testWriteToCard() 
    reader.writeToCard("12345") 
    // assert _mockAPI.writeToCard was called with expected data and flags. 
+0

申し訳ありませんが、私はC# 。また、Javaでは、静的メソッドのようなDIを使っていますが、静的メソッドをメソッド内にラップしてから、テストスタブでラッパーメソッドをアサーションすることができます。 –

+0

ここでは物事を嘲笑いしても役に立つとは思われません。カードを置いたら、私はカードに書き込むことはできません。カードは物理的にそこになければなりません。だから、私は本当に 'WriteToCard'をテストする前に、プリンタがオンになっているか、カードが適切に供給されているかなどに依存します。 私はこのテストを統合テストと呼び、それを残りのテストと区別することにします。 – Evgeny

3

複数のものが壊れている場合は、最初のテストはなり失敗するので、あなたは、障害の完全なリストを取得することはできませんそれ以外のお互いに依存して一連のテストで間違って、本質的には何も、ありません1つは報告した。

これを修正できる方法の1つは、単一のテストを行う前にシステムを既知の状態にするテスト初期化ルーチン([TestFixture]クラスの[SetUp]属性を使用)を作成することです。

このシナリオは、ソフトウェアの手作業による可能性があるため、単体テストには完全には適していません。ユニットテストは、再現性のないものと対話しないソフトウェアモジュールのテストに本来的に適しています。読者APIの抽象的な操作を行うことができます(必要な操作のインターフェイスを作成し、これらの呼び出しを実際のAPIに渡すクラスを作成します)。次にモックオブジェクトを使用してハードウェアに依存することなく、クラスのメインロジックをテストすることができます。

次に、実際の実際のAPIのテストを単体テストや、人間とのやりとりを最小限に抑える必要があるものに実装することができます。基本的にテストプロセスで人をカプセル化します。 )

4

これはユニットテストのようには見えません。単体テストは迅速かつ断言的でなければなりません。つまり、ハードウェアで実際に動作が起こったかどうかを確認する必要はありません。このコードを「テストの自動化」として分類します。このタスクを実行し、何かが起こったことを確認する必要があります。

このコードは手続き型であり、テストするのが難しいようです。同じテスト方法で複数のアサーションを使用すると、それを分割する必要があることが示されます。

ユニットテスト用の私の推奨参照番号はMisko Hevery's siteです。それが役に立てば幸い!

+0

これは私の質問についてです - それは本当に単体テストではありません!私はより多くのユニットテストのように見える他のテスト、すなわち [テスト] ます。public void ConverLongStringToHexArray() { バイト[]期待=新しいバイト[] {0x31、0x32の、0x33の、0x34の、0x35の0x31、0x32のを、持っています0x33、0x34、0x35、0x31、0x32、0x33、0x34、0x35、0x36}; バイト[]結果= reader.StringToHexArray( "1234512345123456zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); Assert.AreEqual(予想される結果、「長い文字列をバイト配列に変換中のエラー」); } 大きなものは分けるのが簡単ではありません リンクbtwをありがとう、非常に便利です – Evgeny

+0

コメントにコードを入れることは悪い考えです... – Evgeny

関連する問題