Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Moq to verify a parameter of type List<>?

Tags:

c#

moq

Using Moq, I'd like to be able to verify that certain conditions are met on a parameter being passed to a mocked method call. In this scenario, I'd like to check that the list passed into the mocked method is of a certain size:

var mockSomeRepository = new Mock<SomeRepository>(); mockSomeRepository.Setup(m => m.Write(It.IsAny<List<SomeDTO>>())).Verifiable();  var mainClass = new MainClass(mockSomeRepository.Object); List<SomeDTO> someList = GetListWith25Items();  mainClass.DoRepositoryWrite(someList); // calls SomeRepository.Write(someList);  mockSomeRepository.Verify(m =>      m.Write(It.Is<List<SomeDTO>>(l => l.Count() == 25)), Times.Once()); 

The verify assert throws an exception that says the method is never called in this fashion. However, removing the constraint and using Is.Any<List<SomeDTO>>() instead leads to a pass. I'm not sure if I'm using It.Is<>() properly here - this is what I intuitively want my test to look like but I'm not sure if I'm using the framework properly. How should I properly frame this test?

like image 487
Chris Trombley Avatar asked Oct 31 '11 14:10

Chris Trombley


People also ask

How can we use a MOQ to check if a method is called with the correct parameters?

When you need to verify that the code under test called a method with the expected parameters, you can mock the method with Moq and use Verify() + It.Is<T>() to check the parameters passed in. Verify() asserts that the method call happened as expected with the specified parameters.

What does MOQ verify do?

Verifies that all verifiable expectations have been met.

How do you mock a method in MOQ?

First, we instantiate the FakeDbArticleMock class and indicate which setup we want to use for this test. Then, it is necessary to instantiate the repository we want to test and inject the mock instance into it. Finally, we call the method we are testing and assert the results.

How do you mock a list in C#?

you can clone it in the same way,or instead you can copy the list yourself by creating a new list and add a copy of each element from the source list. hope it helps.


2 Answers

You can get rid of the call to Setup and Verifiable on your mock. Just use Verify.

I created a little test-project, and this worked for me:

using System; using System.Collections.Generic; using System.Linq; using Moq;  namespace csharp {     public class SomeDTO { }      public class SomeRepository { public virtual void Write(List<SomeDTO> list) { } }      public class MainClass     {         private SomeRepository someRepository;          public MainClass(SomeRepository someRepository) { this.someRepository = someRepository; }          public void DoRepositoryWrite(List<SomeDTO> list) { this.someRepository.Write(list); }     }      class Program     {         static void Main(string[] args)         {             var mockSomeRepository = new Mock<SomeRepository>();             var someList = Enumerable.Repeat(new SomeDTO(), 25).ToList();              var mainClass = new MainClass(mockSomeRepository.Object);             mainClass.DoRepositoryWrite(someList);              mockSomeRepository.Verify(m => m.Write(It.IsAny<List<SomeDTO>>()), Times.Once(), "Write was not called");             mockSomeRepository.Verify(m => m.Write(It.Is<List<SomeDTO>>(l => l.Count == 25)), Times.Once(), "Write was not called with a 25-element-list");         }     } } 
like image 129
sloth Avatar answered Oct 02 '22 09:10

sloth


When I posted this question, I was missing a few important details. I want to elaborate on what was actually happening in case it will help someone in the future. My method under test was actually clearing the list that was being passed to the mock:

public class SomeDTO { }  public class SomeRepository {     public virtual void Write(IEnumerable<SomeDTO> list) { } }  public class MainClass {     private readonly SomeRepository _someRepository;     private readonly List<SomeDTO> _testList = new List<SomeDTO>();       public MainClass(SomeRepository someRepository)     {         _someRepository = someRepository;     }      public void DoRepositoryWrite()     {         _testList.AddRange(Enumerable.Repeat(new SomeDTO(), 25));         _someRepository.Write(_testList);         _testList.Clear();     } }  class Program {     static void Main(string[] args)     {         var mockSomeRepository = new Mock<SomeRepository>();          var mainClass = new MainClass(mockSomeRepository.Object);          mainClass.DoRepositoryWrite();          mockSomeRepository.Verify(m => m.Write(It.IsAny<IEnumerable<SomeDTO>>()), Times.Once(), "Write was not called");         mockSomeRepository.Verify(m => m.Write(It.Is<IEnumerable<SomeDTO>>(l => l.Count() == 25)), Times.Once(), "Write was not called with a 25-element-list");     } } 

Looking back it seems a little obvious, but the takeaway here for me is that the mock hangs on to a reference to the list it was passed. Therefore you need to be careful with any side effects that alter that list.

To write the test properly, I needed to inspect the properties of the passed list immediately when the mocked method was called. To do this I used a callback:

int listCountAtTimeOfCall = 0; mockSomeRepository.Setup(     m => m.Write(It.IsAny<IEnumerable<SomeDTO>>())).Callback         <IEnumerable<SomeDTO>>(list => listCountAtTimeOfCall = list.Count());  ... do the work ...  Assert.AreEqual(listCountAtTimeOfCall, 25); 
like image 30
Chris Trombley Avatar answered Oct 02 '22 09:10

Chris Trombley