在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; } } 

在这种情况下,如何通过IProductDataAccess接口为CreateProduct方法创建unit testing? 我想在ProductBusiness中有一个IProductDataAccess的公共实例,并使用Mock对象对其进行初始化,但是将数据访问暴露给UI层并不是一个好习惯。 谁能帮我?

经典示例,显示如何无法对特定组件进行unit testing,REFACTOR组件!

这就是爱的任何模拟框架强制你做的事情 – 编写解耦代码。

在您的示例中, ProductBusiness类与ProductDataAccess紧密耦合。 您可以使用(如大多数答案所示)dependency injection来解耦它。 通过这样做,您最终将取决于IProductDataAccess抽象,而不是任何具体的实现。

另外需要注意的是,当您编写业务层的测试/规范时,通常需要测试“行为”而不是“状态”。 因此,虽然您可以使用断言来validation是否返回了“true”,但您的测试应该真正测试是否使用MOQ我们实际使用MOQ实际执行的MOQ设置的预期数据访问调用。

尝试添加行为测试,您希望数据访问层抛出exception(使用“.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(); mockDataAccess.Setup(m => m.CreateProduct(It.IsAny())).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; } } 

然后你可以在测试中用mock替换它:

 var productDataAccess = new Mock(); var productBusiness = new ProductBusiness(productDataAccess.Object); 

使用当前设计ProductBusiness类的方式, IProductDataAccess使用mock更改IProductDataAccess实现。 推荐的模式是dependency injection ,您可以通过构造函数获取类型的依赖关系。 所以你的class级变成:

 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(); mockDataAccess .Setup(m => m.CreateProduct(It.IsAny())) .Returns(true); var productBusiness = new ProductBusiness(mockDataAccess.Object); // ... test behaviour here 

现在,您可以更改模拟在设置步骤中的行为方式,并确保CreateProduct方法的行为正确。

我还会看一下像castle-windsor这样的dependency injection框架。 dependency injection框架可以自动解决依赖关系,这意味着创建新类型更容易,因为您不必手动新建所有内容。 此外,它意味着您可以更改在一个位置使用的实现,并在任何地方进行更改。

您不应在CreateProduct方法中实例化具体的ProductDataAccess 。 相反, IProductDataAccess应该是一个可注入的依赖项 。 这可以通过以下两种方式之一完成:

物业注入:

 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 ,并使用该属性可选地注入工作的记录器)。