ハードコードされたオブジェクトでメソッドをモックする方法は?


11

私は複数のレイヤーを持つアプリケーションに取り組んでいます。データソースからデータを取得して保存するデータアクセスレイヤー、データを操作するビジネスロジック、画面にデータを表示するユーザーインターフェイス。

ビジネスロジックレイヤーの単体テストも行っています。唯一の要件は、ビジネスレイヤロジックのフローをテストすることです。そこで、Moqフレームワークを使用してデータアクセスレイヤーをモックし、MS Unitでビジネスロジックレイヤーを単体テストします。

インターフェイスプログラミングを使用して、ユニットテストを実行できるように、設計を可能な限り分離します。インターフェイスを介したビジネスレイヤコールデータアクセスレイヤ。

ビジネスロジックメソッドの1つをテストしようとすると、問題に直面しています。このメソッドはいくつかの作業を行い、オブジェクトを作成してデータアクセスレイヤーに渡します。そのデータアクセスレイヤーメソッドをモックしようとすると、正常にモックできません。

ここでは、問題を示すデモコードを作成しようとしています。

モデル:

public class Employee
{
    public string Name { get; set; }
}

データアクセス層:

public interface IDal
{
    string GetMessage(Employee emp);
}

public class Dal : IDal
{
    public string GetMessage(Employee emp)
    {
        // Doing some data source access work...

        return string.Format("Hello {0}", emp.Name);
    }
}

ビジネスロジックレイヤー:

public interface IBll
{
    string GetMessage();
}

public class Bll : IBll
{
    private readonly IDal _dal;

    public Bll(IDal dal)
    {
        _dal = dal;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method.
        Employee emp = new Employee(); 

        string msg = _dal.GetMessage(emp);
        return msg;
    }
}

単体テスト:

[TestMethod]
    public void Is_GetMessage_Return_Proper_Result()
    {
        // Arrange.
        Employee emp = new Employee; // New object.

        Mock<IDal> mockDal = new Mock<IDal>();
        mockDal.Setup(d => d.GetMessage(emp)).Returns("Hello " + emp.Name);

        IBll bll = new Bll(mockDal.Object);

        // Act.

        // This will create another employee object inside the 
        // business logic method, which is different from the 
        // object which I have sent at the time of mocking.
        string msg = bll.GetMessage(); 

        // Assert.
        Assert.AreEqual("Hello arnab", msg);
    }

モック作成時の単体テストケースでは、Employeeオブジェクトを送信していますが、ビジネスロジックメソッドを呼び出すと、メソッド内に別のEmployeeオブジェクトが作成されます。それがオブジェクトをモックできない理由です。

その場合、問題を解決できるように設計する方法は?


通常、トリックはオブジェクトをインターフェイスでラップし、そのすべてのコンシューマがそのインターフェイスを使用するようにし、インターフェイスをモックするか、メソッドを仮想化し、moqがインターフェイスなしでメソッドをモックすることです。ただし、この場合のrhinomocksまたはその他については定かではありません。
ジミー・ホッファ

回答:


12

代わりに作成するのでEmployee使用してオブジェクトを直接new、あなたのクラスがBll使用できるEmployeeFactory方法で、このためにクラスをcreateInstanceコンストラクタを介して注入され、:

 class EmployeeFactory : IEmployeeFactory
 {
       public Employee createInstance(){return new Employee();}
 }

コンストラクターは、インターフェイスを介してファクトリーオブジェクトを取得する必要がありますIEmployeeFactory。そのため、「実際の」ファクトリーをモックファクトリーに簡単に置き換えることができます。

public class Bll : IBll
{
    private readonly IDal _dal;
    private readonly IEmployeeFactory _employeeFactory;

    public Bll(IDal dal, IEmployeeFactory employeeFactory)
    {
        _dal = dal;
        _employeeFactory=employeeFactory;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method
        // *** using a factory ***
        Employee emp = _employeeFactory.createObject(); 
        // ...
    }
    //...
}

モックファクトリは、テストに必要なあらゆる種類のEmployeeオブジェクトをテストに提供できます(たとえば、createInstance常に同じオブジェクトを返すことができます)。

 class MockEmployeeFactory : IEmployeeFactory
 {
       private Employee _emp;

       public MockEmployeeFactory()
       {
          _emp = new Employee();
          // add any kind of special initializing here for testing purposes
       }

       public Employee createInstance()
       {
          // just for testing, return always the same object
          return _emp;
       }
 }

これで、テストでこのモックを使用してトリックを行う必要があります。


あなたの理論を視覚化できるように、1つのコード例を教えてください。
DeveloperArnab

@DeveloperArnab:編集をご覧ください。
ドックブラウン

非常に役に立っ...
DeveloperArnab

4

私はそれをテストする単一ユニットとして扱います。

Employeeオブジェクトが作成されるすべての入力を制御する限り、テストされたオブジェクトで作成されるという事実は重要ではありません。引数の内容が期待と一致する場合、期待される結果を返すためのモックメソッドが必要です。

明らかに、モックメソッドにカスタムロジックを提供する必要があることを意味します。高度なロジックは、「for x return y」という種類のモックだけではテストできないことがよくあります。

実際、テストで異なるオブジェクトを返すようにすべきではありません。実動した場合、それを作成するコードをテストすることはないからです。ただし、そのコードは製品コードの不可欠な部分であるため、テストケースでもカバーする必要があります。


はい、データアクセス層の入力については気にしません。ビジネスロジックをテストできるように、そのオブジェクトをモックし、ハードコードされたデータを返したいだけです。しかし、問題は2つの異なるEmployeeオブジェクトのためであり、データアクセスレイヤーメソッドをモックすることはできません。
DeveloperArnab

@DeveloperArnab:オブジェクトは異なりますが、既知のコンテンツが含まれます。したがって、オブジェクトIDの代わりにモックにカスタム比較を実行させるだけです。
ジャン・ヒューデック

@DeveloperArnab:Employeeテストで別のオブジェクトを注入する場合、通常はそれを作成するコードをテストしません。したがって、変更しないでください。
ジャン・ヒューデック

0

いくつかのテストツールでは失敗したため、常にインターフェイスを使用する必要があり、インターフェイスベースのオブジェクトを別のオブジェクトに交換できるようにすべてを作成する必要があります。

ただし、より優れたツールがあります。MicrosoftFakes(以前はMolesと呼ばれていました)を使用すると、静的オブジェクトやグローバルオブジェクトを含む任意のオブジェクトをスワップアウトできます。オブジェクトを置き換えるには、より低レベルのアプローチが必要です。そのため、慣れ親しんだテストを記述する方法を維持しながら、どこでもインターフェイスを使用する必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.