私は次のコードを持っています:
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レイヤーに公開することはお勧めできません。誰でも私を助けることができますか?
特定のコンポーネントを単体テストできない場合に、コンポーネントを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
依存関係として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);
現在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つの場所で使用するかを変更でき、どこでも変更できることを意味します。
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
をインスタンス化し、プロパティを使用して作業ロガーをオプションで注入します)。