简体   繁体   中英

Mocking using Moq in c#

I have the following code:

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

Class ProductDataAccess implements that interface.

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

In this case, how to create unit test for CreateProduct method by mocking the IProductDataAccess interface? I thought of having an public instance of IProductDataAccess within ProductBusiness and initialize it using Mock<IProductDataAccess> object but it is not a good practice to expose the data access to the UI layer. Can any one help me?

Classic example which shows how if you cannot unit test a particular component, REFACTOR the component!

This is where is love what any mocking framework enforces you to do - write decoupled code.

In your example, the ProductBusiness class is very tightly coupled with the ProductDataAccess . You could decouple it using (like most of the answers suggest) dependency injection. By doing so, you would end up depending on the IProductDataAccess abstraction than any concrete implementation of it.

Another point to note, when you are writing tests/specifications for the business layer, you would typically want to test the "behavior" and not the "state". So, though you could have asserts that verify if "true" was returned, your tests should really test if the expected data access calls that were set using MOQ we actually executed using the ".Verify" API of MOQ.

Try adding behavior tests where you expect an exception to be thrown(using the ".Throws" API) by the data access layer and check if you need any special handling at the business layer.

Like Kevin suggests, have the ProductBusiness which looks like:

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

and use any xunit testing framework to write the test as:

 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

You should inject IProductDataAccess interface as a dependency:

public class ProductBusiness
{
    private IProductDataAccess _productDataAccess;    

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

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

Then you can replace it with a mock in your tests:

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

With the way that you have currently designed your ProductBusiness class there is no way of changing the IProductDataAccess implementation using a mock. A recommended pattern for this is where you take the dependencies of a type through the constructor. So your class becomes:

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

Now you are in a position to test your class by using a mocking framework like . For example:

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

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

Now you can change how the mock behaves in your setup step and make sure that your CreateProduct method is behaving correctly.

I would also look at a dependency injection framework like . A dependency injection framework can automatically resolve dependencies meaning creating a new type is much easier as you don't have to manually new everything up. Also it means that you can change which implementation is used in one place and it changes everywhere.

You shouldn't instantiate a concrete ProductDataAccess inside your CreateProduct method. Instead, IProductDataAccess should be an injectable dependency . This can be done in one of two ways:

Property injection:

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

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

Or constructor injection:

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());

Constructor injection is usually the recommend approach. Property injection is used for optional dependencies (eg, instantiate a concrete NullLogger by default in the constructor, and use the property to optionally inject a working logger).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM