首页 > 解决方案 > Verifying service calls IRepository method that is defined in IGenericRepository

问题描述

I have a service let's say Foo service class.

public class FooService : IFooService
{
    private readonly IFooRepository _repository;
    private readonly ISomeService _eventService;

    public FooService(IFooRepository repository, ISomeService eventService)
    {
        _repository = repository;
        _someService = someService;
    }

    public IReadOnlyCollection<Foo> GetFoos(bool isDeleted = true)
    {
        var foos= _repository.GetList(x => x.IsDeleted == isDeleted).ToList();
        return !foos.Any() ? new List<Foo>(): foos;
    }

}

Here is IFooRepository

public interface IFooRepository : IGenericRepository<Foo>
{

}

and here is IGenericRepository

public interface IGenericRepository<T> where T: BaseEntity
{

    IReadOnlyCollection<T> GetList(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] nav);

}

In my test I want to verify that FooService's GetFoos method calls GetList method

This is what I tried

[TestClass]
public class FooServiceTest
{
    private IQueryable<Foo> _foos;
    private Mock<IFooRepository> _fooRepository;
    private FooService _fooService;
    private Mock<ISomeService> _someService;
    [TestInitialize]
    public void SetUp()
    {

        _foos = new List<Foo>
        {
            new Foo
            {                  
                EmailId = "a@a.com",                  
                IsDeleted = false,                 
            },
            new Foo
            {                  
                EmailId = "a@a.com",                  
                IsDeleted = true,                 
            },       
        }.AsQueryable();           
    }

    [TestMethod]
    public void GetGetFoos_CallsGetList()
    {
        //Arrange
        var foos= _foos.Where(x => x.IsDeleted).ToList();    
        _fooRepository = new Mock<IFooRepository>();         
        _fooRepository.Setup(m => m.GetList(x => x.IsDeleted)).Returns(foos);
        _someServiceMock = new Mock<ISomeService>();
        _fooService = new FooService(_fooRepository.Object, _someServiceMock.Object);
        //Act
        _fooService.GetFoos(true);

        //Assert
        _fooRepository.Verify(m=>m.GetList(x=>x.IsDeleted), Times.Once());

    }
}

But I get argument null exception in following line

var foos= _repository.GetList(x => x.IsDeleted == isDeleted).ToList();

Any clue why this is happening even though I am saying Returns(foos) during setup.

Also how do I verify the interface method was called?

标签: c#unit-testingmoq

解决方案


What is happening (most likely) is Moq can't match Expression<Func<T, bool>> when you do the .Setup().

So instead you can use IsAny<>() approach:

_fooRepository.Setup(m => m.GetList(It.IsAny<Expression<Func<Foo, bool>>>())).Returns(foos);

If you want to assert what expression is passed in, try

Expression<Func<Foo, bool>> capturedExpression = null; 
_fooRepository.Setup(m => m.GetList(It.IsAny<Expression<Func<Foo, bool>>>()))
.Returns((Expression<Func<Foo, bool>> e ) => { capturedExpression = e; return foos; }); 
Assert.IsTrue(capturedExpression.Compile()(_foos[1])); 
Assert.IsFalse(capturedExpression.Compile()(_foos[0]));

to verify the method was called you can also change the last it a bit more:

_fooRepository.Setup(m => m.GetList(It.IsAny<Expression<Func<Foo, bool>>>()))
.Returns((Expression<Func<Foo, bool>> e ) => { capturedExpression = e; return foos; })
.Verifiable(); 

then _fooRepository.Verify(m=>m.GetList(It.IsAny<Expression<Func<Foo, bool>>>()), Times.Once()); However, if it is not called, then capturedExpression is null (that technique is known as implicit assertion)


推荐阅读