2

In my controller action, whenever a new product is added I check in the database that this product no is not present already. The code for this check looks like

 public ActionResult Index(ProductModel model)
    {
      var productCount = _productsService.GetAll(true).Count(x => x.ProductNumber == model.ProductNumber);

      if (productCount > 0)
          ModelState.AddModelError("ProductNumber", Product already present in the system!");

       // more processing
     }

I m new to MOQ testing and trying to write a unit test to setup the GetAll method which will return 0. I have written something like this but it does not seem to work

 var _productsService = new Mock<IProductsService>();
_productsService.Setup(m => m.GetAll(true).Count()).Returns(0);

Any ideas? thanks

k.m
  • 30,794
  • 10
  • 62
  • 86
rumi
  • 3,293
  • 12
  • 68
  • 109

2 Answers2

2

This is not how you use Moq -- Count is most likely not your method (it's LINQ/other 3rd party), you don't mock it. What you need to mock is GetAll method, which is a method on your mockable dependency. You "tell" GetAll to return a product with model matching the parameter, like so:

[Test]
public void Index_ReportsModelError_WhenProductAlreadyExists()
{
    const int ExistingProductNumber = 10;
    var _productsService = new Mock<IProductsService>();
    var existingProduct = new Product { ProductNumber = ExistingProductNumber };
    _productsService.Setup(m => m.GetAll(true)).Returns(new [] { existingProduct });

    controller.Index(new ProductModel { ProductNumber = ExistingProductNumber });

    // Assert
}

Fixing existing tests is as simple as making sure GetAll never returns products with number same as the one in Index parameter:

const int ExistingProductNumber = 10;
const int NewProductNumber = 20;
var _productsService = new Mock<IProductsService>();
var existingProduct = new Product { ProductNumber = ExistingProductNumber };
_productsService.Setup(m => m.GetAll(true)).Returns(new [] { existingProduct });

controller.Index(new ProductModel { ProductNumber = NewProductNumber });

// Assert
k.m
  • 30,794
  • 10
  • 62
  • 86
  • I m working on some one else test code. I had to add this check (i.e. the product number is not already present) in the controller action and now these unit tests are failing as a result of this change. I m now trying to work out how i can by pass 'this addition of GetAll check and adding this error in the ModelState'. I want to setup something like, this code always by passes in the unit tests which were written prior to when this check was added. And yes certainly i can add a new Unit test too to check GetAll method on the repository. – rumi Apr 01 '15 at 11:51
  • @Learner: *"And yes certainly i can add a new Unit test too to check GetAll method on the repository"* -- why would you do that? You test controller, not `GetAll`. `GetAll` is dependency to your controller, you stub it (as you do now). You mentioned you added **new feature** which broke tests. This requires change in tests -- you need to make sure existing setups of `GetAll` never return products with same number as the one being passed to `Index` method. – k.m Apr 01 '15 at 11:56
  • Thanks @jimmy_keen you have clarified my concept on this. Any recommendation on good tutorials online or book or videos on Moq testing with repository? – rumi Apr 01 '15 at 12:21
  • @Learner: to get along with Moq I suggest their wiki page -- https://github.com/Moq/moq4/wiki/Quickstart. This particular case (repository testing) does not require any special tutorials IMHO... repository is just dependency which you mock. You'd be better of looking for tutorials on ASP.NET MVC controller unit testing, which there are plenty online. – k.m Apr 01 '15 at 12:37
1

Take a look at this post: Expression references a method that does not belong to the mocked object

He basically has the same problem, you're trying to mock an extension method Count()

Community
  • 1
  • 1
greenhoorn
  • 1,601
  • 2
  • 15
  • 39