2016-12-15 7 views
0

クラスControllerがクラスServiceに依存し、ServiceクラスがクラスRepositoryに依存するとします。 Repositoryだけが外部システム(例えばDB)と通信し、単体テストが実行されたときにそれを嘲笑すべきだと私は知っています。単体テストでクラスのすべての直接の依存関係を模擬すべきですか?

私の質問:Serviceクラスが外部システムに直接依存していないにもかかわらず、Controllerクラスがテストされたときに単体テストの場合、Serviceクラスを模擬すべきですか?なぜ?

答えて

2

これは、作成しているテストの種類によって異なります:統合テストまたは単体テスト。 この場合、単体テストを書いたとします。単体テストの目的は、クラスのビジネスロジックを単独でテストし、他のすべての依存関係を嘲笑することです。

この場合、Serviceクラスをモックします。そうすることで、特定のメソッドに渡す入力に基づいて特定のシナリオをテストするための準備をすることもできます(Service)。あなたのServicePerson findPerson(Long personID)の方法があるとします。 Controllerをテストするときには、Serviceが実際に正しい出力を返すために必要なすべてを行うことには興味がありません。 Controllerのテストシナリオでは、Personを返すだけですが、別のテストシナリオでは何も返さないようにしてください。モッキングはこれを非常に簡単にします。

Serviceを偽装する場合は、Serviceがすでに偽であるため、Repositoryを偽装する必要はありません。

TLDR;特定のクラスの単体テストを書くときは、他のすべての依存関係をモックして、これらの依存関係に対して行われたメソッド呼び出しの出力を操作できるようにしてください。

+0

あなたの前提は正しかったです:私はユニットテストを書いています。そして今、私は単体テストの仕方を知りました。どうもありがとうございました! – umainyosu

1

はい、モックコントローラをテストするときのサービス。単体テストは、回帰の位置を特定するのに役立ちます。したがって、サービスコードのテストは、サービスコードが変更された場合にのみ失敗し、コントローラコードが変更された場合には失敗しません。このようにして、サービステストが失敗すると、根本的な原因がサービスの変更にあることを確実に知ることができます。

また、通常は、サービスを呼び出すだけで、サービスを呼び出すすべてのリポジトリを模擬してコントローラをテストするよりもはるかに簡単です。そのため、テストを簡単に保つことができます。

しかし、一般的に、ある種のutilクラスはモックされていないので、あなたはそれらを嘲笑して得るよりも緩いので、ロックされているかもしれません。また、参照してください: https://softwareengineering.stackexchange.com/questions/148049/how-to-deal-with-static-utility-classes-when-designing-for-testability

0

すべてのエンジニアリングに関する質問と同様、TDDも同じです。答えは常に「それは依存する」です。常にトレードオフがあります。

TDDの場合は、まず行動的な期待からテストを開発します。私の経験では、行動的期待は単位です。

たとえば、姓が「A」で始まるすべてのユーザーを取得したい場合、システムでアクティブになっているとします。したがって、コントローラアクションを作成してアクティブユーザーを取得するテストを作成し、 'A'で始まるものをpublic ActionResult GetAllActiveUsersThatStartWithA()とします。最後に

、私はこのようなものかもしれません。私に

public ActionResultGetAllActiveUsersThatStartWithA() 
{ 
    var users = _repository.GetAllUsers(); 
    var activeUsersThatStartWithA = users.Where(u => u.IsActive && u.Name.StartsWith('A'); 
    return View(activeUsersThatStartWithA); 
} 

これを単位です。私は、今、リファクタリング

public IEnumerable<User> GetActiveUsersThatStartWithLetter(char startWith) 
{ 
    var users = _repository.GetAllUsers(); 
    var activeUsersThatStartWithA = users.Where(u => u.IsActive && u.Name.StartsWith(startsWith); 
} 

とコントローラの私の新しい実装(下記の方法でserviceクラスを追加することで動作を変更することなく、私の実装を変更)することができます。これは明らかに非常に不自然である

public ActionResultGetAllActiveUsersThatStartWithA() 
{ 
    return View(_service.GetActiveUsersThatStartWithLetter('A'); 
} 

なりしかし、それは私の考えのアイデアを与える。このようにすることの主な利点は、私のテストがrepository以外の実装の詳細に縛られていないことです。私のテストでserviceを嘲笑した場合、私は今この実装に縛られています。どんな理由であれserviceレイヤーが削除されても、すべてのテストが中断します。私は、serviceレイヤーがrepositoryレイヤーよりも変化する可能性が高いことがわかります。

コントローラのクラスでserviceを模倣すると、すべてのテストが正常に動作するシナリオに遭遇する可能性がありますが、システムが見つかった唯一の方法は統合テスト(アウトオブプロセスまたはアセンブリコンポーネントが相互に作用することを意味します)、または生産上の問題によって発生します。例えば、私は以下にserviceクラスの実装を変更した場合

は:再び

public IEnumerable<User> GetActiveUsersThatStartWithLetter(char startsWith) 
{ 
    throw new Exception(); 
} 

、これは非常に不自然な例ですが、ポイントはまだ関連しています。私はcontrollerテストでこれをキャッチしません。したがって、システムが私の渡す "ユニットテスト"で正しく動作しているように見えますが、実際にはシステムは全く動作していません。

私のアプローチの欠点は、テストがセットアップに非常に面倒になる可能性があることです。したがって、テストの複雑さを抽象化/モック可能な実装とバランスさせることがトレードオフです。

TDDは回帰を捉えるという利点がありますが、システムの設計には大きな利点があります。言い換えれば、あなたが書いたテストをデザインに指示させてはいけません。テストがシステムの機能を最初に指示してから、リファクタリングによる設計について心配してください。

関連する問題