6

私は自分のプロジェクトに単体テストを追加し、テスト駆動型の方法で開発を続けることにしました。現在、ManageSieveクライアントオブジェクトの単体テストの実装に取り​​組んでいますが、その獣をテストするための最良の方法がわかりません。

SieveClientのオブジェクトは、ネットワーク通信のために他の2つのオブジェクトに依存しています。1つは、認証メソッドを処理するためのCyrusSASLライブラリのラッパーであるCocoaAsyncSocket自分のオブジェクトです。SaslConnテストのために、それらをモックオブジェクトに置き換える必要があります。これにはOCMockフレームワークを使用します。SieveClientオブジェクトはそれらのオブジェクト自体を作成する必要があるため、これを行う方法はよくわかりません。現在、そのオブジェクトの(プライベート)セッターを上書きして、常にOCMockspartialMockForObject:メソッドを使用してモックオブジェクトをインストールしています。しかし、これは私には正しくないと感じています。これをよりよく解決する方法はありますか?

私が問題を抱えている他の部分は、ソケット自体です。プロトコルの詳細をテストできるようにするには、ソケットから事前定義されたテストデータを返す方法が必要です。OCMockメカニズムを使用して、ソケットからの戻り値を偽造することができると思います。しかしCocoaAsyncSocket、ソケットからデータを読み取るためのさまざまなメソッドが提供されているため、プロトコルオブジェクトによってどの順序で使用されているかを正確に知る必要があります。単体テストをプロトコルオブジェクトの実装の詳細に依存させたくありません。だから私はここで何をすべきですか?ソケットクラスのモックオブジェクトを手動で実装しますか?これは些細なことではないように思われるので、おそらくそのための単体テストも必要になるでしょう。それはいい考えですか?

何かをテストするのが難しい場合、それもおそらくあまりうまく設計されていないことを私は読んだ。しかし、難しいのは私がしなければならないソケットとの相互作用にあるので、どうすればもっとうまくできるかわかりません。

コードを確認したい場合は、Bitbucket:SieveClient.mおよびSieveClient.hで見つけることができます。

編集:依存性注入

だから私は依存性注入について読みました、そして私はこれを使ってAsyncSocketSaslConnオブジェクトを私のSieveClientオブジェクトに入れるつもりだと思います。これらのオブジェクトを受け入れて使用するようにコンストラクターを変更します。このクラスのユーザーは通常、ソケットとSASLオブジェクトを気にしないので、これらのオブジェクトを作成してコンストラクターに渡すだけのファクトリメソッド(コンビニエンスコンストラクターの形式)を追加します。

しかし、これは私のテスト問題の最初の(そしてより簡単な)部分だけを解決します。

4

3 に答える 3

7

しかし、それはあまり役に立たないので、私はそのアイデアを拒否しました。SieveClientオブジェクトをもっと簡単にテストできました。しかし、新しいオブジェクトのテストでも同じ問題が発生します。これは後で問題を引き起こしているだけだと私には思えます。特に私には何もないので、新しいクラスを再利用することができました。

同じ問題ではないでしょう。

他のオブジェクトのインスタンス化を内部的に制御するには、SieveClientが必要であると想定します。これは、公開したくないAPIの一部であるためです。それが理由である場合、それらを分離することにより、SieveClientにバインディングを制御させることができ、プロトコルを実行する他の部分がそれが動作するインスタンスを受信するため、同じ必要性はなくなります。

上記を実行することにより、モックされたオブジェクトをプロトコル実装に渡すことができます。それらのモックは、あなたが必要とするかもしれないどんな期待も持っているでしょう。関与しすぎていることに気付いた場合は、おそらく責任に再び焦点を合わせる必要があります。これにより、通常、プロトコルの実装がよりクリーンでシンプルになります(これらの単体テストを実行する必要があることがわかった場合)。

上記のように、テストしようとしているコードがプロトコルに可能な限り焦点を合わせており、余分な要素がないかどうかも考慮する必要があります。その場合、その唯一の責任は外部システムとの相互作用であるため、単体テストを行うのは適切な候補ではありません。このシステムのプロトコル仕様がどれほど重要かを判断し、外部システムとの統合がすべてである場合は、実際の外部システムにヒットし、単体テストから分離されたままの、焦点を絞った統合テストのように扱います(したがって、システムの他の部分の単体テストを実行するために必要な速度には影響しません)。


編集のために質問を読み直した後、上記の集中統合テストについて私が言ったことを強調する必要があります。あなたが尋ねる:

しかし、CocoaAsyncSocketはソケットからデータを読み取るためのさまざまなメソッドを提供するため、プロトコルオブジェクトによってどの順序で使用されているかを正確に知る必要があります。単体テストをプロトコルオブジェクトの実装の詳細に依存させたくありません。だから私はここで何をすべきですか?ソケットクラスのモックオブジェクトを手動で実装しますか?これは些細なことではないように思われるので、おそらくそのための単体テストも必要になるでしょう。それはいい考えですか?

非常に複雑なオブジェクトを扱っていて、そのオブジェクトがすべて境界を越えた統合に関するものである場合、通常、単体テストの一部としてそれを回避するのが最善です。そのシナリオでは、焦点を絞った統合テストを/実際の外部システムに当てはめる必要があります。これは、残りのコードのすべての単体テストを意味するのではなく、外部システムにヒットします。そのオブジェクトを使用するコード/クラスの非常に単純な単位だけです。

そのようなオブジェクトがシナリオのSieveClientである場合が非常によくあります。その場合、コードのその部分の単体テストを忘れてください。代わりに実行したいのは、SieveClientを使用するコードをテストするときにSieveClientをモックすることです。一方、SieveClientがそれ以上のものであることがわかった場合は、これらの通信の側面を単純化するクラスを追加する必要があります。これは、SieveClientをテストするときにモックするものであり、焦点を絞った統合テストを行うものでもあります。

このタイプのテストは、外部と相互作用するコードが期待どおりに機能していることを確認するための非常に効果的な方法です。これは、クラスと関連するテストの両方の焦点であるためです。外部システム上の何かが異なって動作し始めた場合、それがアプリケーションロジックと混合されたり、最悪の場合はまったくテストされなかったりするのとは対照的に、はっきりと気づきます。

于 2010-09-06T23:00:30.477 に答える
2

実行していることを2つの部分に分割できますか。1つは抽象プロトコルで、もう1つはソケットへのバインドです。次に、抽象プロトコルをより簡単にテストし、バインディングのテストを、接続されている抽象プロトコルのメソッド/操作を正しく呼び出すかどうかに焦点を当てることができます。

抽象的には、コードの各部分間の結合を減らすことになります。これにより、テストの可能性が向上しますが、全体的な複雑さが増し(ただし、関心の分離によって管理するためのより優れたツールが得られるため、それほど悪くはありません)、パフォーマンスが低下する可能性があります(ただし、ほとんどのシステムではそれほど問題にはなりません。お使いのコンピュータは、I / Oサブシステムよりもはるかに高速です)。

于 2010-09-04T11:31:46.080 に答える
2

教義に耳を傾けすぎないでください。テストでも、おそらく機能する可能性のある最も単純なものを探してください。(免責事項:私はTDDを知っていますが、Objective Cを知りません)。

実稼働コードでそのをSieveClient作成しSaslConn、テストでモックを使用するには、依存性注入を使用できます。ファクトリSieveClientを渡すためのsetterメソッドを追加します(Objective Cで許可されているものに応じて、オブジェクトまたは関数として)。これは、ファクトリを単独で作成するのではなく、作成するために使用します。テストコードは、モックを皿に盛るテストファクトリを提供します。を作成するための本番ケースのコードは、別のファクトリに移動して個別に単体テストを行うか、簡単に破ることができない場合は、ファクトリセッターが呼び出されないときのデフォルトの動作として残ります。SieveClientSaslConnSaslConnSieveClient

ネットワーククライアントコードをテストする最も簡単な方法は、モックサーバーを実装または再利用することです。で残酷なソケットの詳細をモックアウトしないでくださいSaslConn。代わりに、テストでSASLサーバーを記述します。あなたがそれに話しかけることができるという事実は、SaslConnそのモックサーバーのテストを提供することに大いに役立ちます。言い換えれば、SaslConnモックサーバーはお互いのユニットテストです。(ええ、純粋な意味での「ユニット」ではありませんが、誰も気にしません。)

最後に、テストが難しいコードは不適切に設計されているという原則について、私はさまざまな感情を抱いています。場合によります。(呼び出し元のコードで)使いやすく、変更しやすいようにコードを設計する必要があります。単体テストは、これらの目的を達成するための手段にすぎません。単体テストは、作成する最初の呼び出し元コードであり、変更を加えるときに失敗しないという自信を与えてくれます。特定のフレームワークや方法論をひねって、TDDの利点を上回るほど設計を損なうことのないようにしてください。特に、OCMockなどの期待に基づくモックフレームワークでは、「メソッドが3回呼び出されてから、メソッドが呼び出されることを期待しています。foobarまさにそのような議論で呼ばれるべきです」。仕事に間違ったツールを使用するのではなく、あなた自身のものを書いてください!

于 2010-09-09T21:28:14.360 に答える