web-dev-qa-db-ja.com

C#でMoqを使用したモック

私は次のコードを持っています:

public interface IProductDataAccess
{
    bool CreateProduct(Product newProduct);
}

クラスProductDataAccessはそのインターフェースを実装します。

public class ProductBusiness
{
    public bool CreateProduct(Product newProduct)
    {
        IProductDataAccess pda = new ProductDataAccess();
        bool result = pda.CreateProduct(newProduct);
        return result;
    }
}

この場合、CreateProductインターフェイスをモックすることでIProductDataAccessメソッドの単体テストを作成する方法は? IProductDataAccess内にProductBusinessのパブリックインスタンスを作成し、Mock<IProductDataAccess>オブジェクトを使用して初期化することを考えましたが、データアクセスをUIレイヤーに公開することはお勧めできません。誰でも私を助けることができますか?

22

特定のコンポーネントを単体テストできない場合に、コンポーネントをREFACTORする方法を示す古典的な例!

これは、モックフレームワークが実行することを強制するものです-分離コードを記述します。

あなたの例では、ProductBusinessクラスはProductDataAccessと非常に密に結合されています。 (ほとんどの答えが示唆するように)依存性注入を使用して分離できます。そうすることにより、IProductDataAccess抽象化に依存することになります。

別の注意点として、ビジネス層のテスト/仕様を作成する場合、通常は「状態」ではなく「動作」をテストします。そのため、「true」が返されたかどうかを検証するアサートを実行できますが、MOQを使用して設定されたデータアクセス呼び出しがMOQの「.Verify」APIを使用して実際に実行されたかどうかをテストする必要があります。

データアクセスレイヤーによって例外がスローされる(「.Throws」APIを使用)と予想される場所に動作テストを追加し、ビジネスレイヤーで特別な処理が必要かどうかを確認してください。

Kevinが提案するように、次のようなProductBusinessがあります。

public class ProductBusiness
{
  private readonly IProductDataAccess  _productDataAccess;

  public ProductBusiness(IProductDataAccess productDataAccess)
  {
      _productDataAccess = productDataAccess;
  }

  public bool CreateProduct(Product newProduct)
  {
    bool result=_productDataAccess.CreateProduct(newProduct);
    return result;
  }
}

xunitテストフレームワークを使用して、テストを次のように記述します。

 var mockDataAccess = new Mock<IProductDataAccess>();
 mockDataAccess.Setup(m => m.CreateProduct(It.IsAny<Product>())).Returns(true);
 var productBusiness = new ProductBusiness(mockDataAccess.Object);
 //behavior to be tested
36
Amol

依存関係としてIProductDataAccessインターフェイスを注入する必要があります。

public class ProductBusiness
{
    private IProductDataAccess _productDataAccess;    

    public ProductBusiness(IProductDataAccess productDataAccess)
    {
        _productDataAccess = productDataAccess;
    }

    public bool CreateProduct(Product newProduct)
    {
        bool result = _productDataAccess.CreateProduct(newProduct);
        return result;
    }
}

その後、テストでモックに置き換えることができます:

var productDataAccess = new Mock<IProductDataAccess>();
var productBusiness = new ProductBusiness(productDataAccess.Object);
22

現在ProductBusinessクラスを設計している方法では、モックを使用してIProductDataAccess実装を変更する方法はありません。これに推奨されるパターンは dependency-injection です。ここでは、コンストラクターを介して型の依存関係を取得します。クラスは次のようになります。

public class ProductBusiness
{
  private readonly IProductDataAccess  _productDataAccess;

  public ProductBusiness(IProductDataAccess productDataAccess)
  {
      _productDataAccess = productDataAccess;
  }

  public bool CreateProduct(Product newProduct)
  {
      bool result = _productDataAccess.CreateProduct(newProduct);
      return result;
  }
}

これで、 moq のようなモックフレームワークを使用して、クラスをテストできます。例えば:

var mockDataAccess = new Mock<IProductDataAccess>();
mockDataAccess
    .Setup(m => m.CreateProduct(It.IsAny<Product>()))
    .Returns(true);

var productBusiness = new ProductBusiness(mockDataAccess.Object);
// ... test behaviour here

これで、セットアップ手順でモックの動作を変更し、CreateProductメソッドが正しく動作することを確認できます。

castle-windsor のような依存性注入フレームワークも見ていきます。依存性注入フレームワークは依存性を自動的に解決できます。つまり、すべてを手動で新しくする必要がないため、新しい型の作成がはるかに簡単になります。また、どの実装を1つの場所で使用するかを変更でき、どこでも変更できることを意味します。

9
Kevin Holditch

ProductDataAccessメソッド内で具体的なCreateProductをインスタンス化しないでください。代わりに、IProductDataAccess注入可能な依存関係でなければなりません。これは、次の2つの方法のいずれかで実行できます。

プロパティ注入:

public class ProductBusiness
{
    IProductDataAccess Pda {get; set;}
}

var productBusiness = new ProductBusiness();
productBusiness.Pda = new ProductDataAccess();
productBusiness.Pda = new MockProductDataAccess();

またはコンストラクター注入:

public class ProductBusiness
{
    private readonly IProductDataAccess _pda;

    public ProductBusiness(IProductDataAccess pda)
    {

        _pda = pda;
    }
}

var productBusiness = new ProductBusiness(new ProductDataAccess());
var productBusiness = new ProductBusiness(new MockProductDataAccess());

通常、コンストラクター注入が推奨されるアプローチです。プロパティの注入は、オプションの依存関係に使用されます(たとえば、コンストラクターでデフォルトで具体的なNullLoggerをインスタンス化し、プロパティを使用して作業ロガーをオプションで注入します)。

6
dcastro