Reputation: 223
I have been read a lot of others QA about this topic and I still can't find the solution to my problem, so I have decide to expose my case.
I have this interface
public interface IRepository<T> where T : class, IEntity
{
IQueryable<T> Find(Expression<Func<T, bool>> predicate);
T FindIncluding(int id, params Expression<Func<T, object>>[] includeProperties);
}
And this is the basic structure of the method that contains the Mock that I would like to setup
public PeopleController CreatePeopleController()
{
var mockUnitofWork = new Mock<IUnitOfWork>();
var mockPeopleRepository = new Mock<IRepository<Person>>();
mockPeopleRepository.Setup(r=>r.Find().Returns(new Person(){});
mockUnitofWork.Setup(p => p.People).Returns(mockPeopleRepository.Object);
return new PeopleController(mockUnitofWork.Object);
}
I have been trying to setup the Mock using this way:
public PeopleController CreatePeopleController()
{
var mockUnitofWork = new Mock<IUnitOfWork>();
var mockPeopleRepository = new Mock<IRepository<Person>>();
mockPeopleRepository.Setup(r=>r.Find(It.isAny<Expression<Func<Person,bool>>>()).Single()).Returns(new Person(){});
mockUnitofWork.Setup(p => p.People).Returns(mockPeopleRepository.Object);
return new PeopleController(mockUnitofWork.Object);
}
But the system always throws the same exception "System.NotSupportedException: Expression references a method that does not belong to the mocked object .... "
Also I would like to add that I am using MSTest and Moq
I know that setup a Mock using Expression is not easy and not recommended, but it is very important for me because "Find" is a method that I use a lot in my app
Upvotes: 15
Views: 9801
Reputation: 13002
Using Moq's It.IsAny<>
without a .CallBack
forces you to write code that's not covered by your test. Instead, it allows any query/expression at all to pass through, rendering your mock basically useless from a unit testing perspective.
The solution: You either need to use a Callback to test the expression OR you need to constrain your mock better. Either way is messy and difficult. I've dealt with this issue for as long as I've been practicing TDD. I finally threw together a helper class to make this a lot more expressive and less messy. Here's the end-result (adapted to your example):
mockPeopleRepository
.Setup(x => x.Find(ThatHas.AnExpressionFor<Person>()
.ThatMatches(correctPerson)
.And().ThatDoesNotMatch(deletedPerson)
.Build()))
.Returns(_expectedListOfPeople);
Here's the blog article that talks about it and gives the source code: http://awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/
Upvotes: 5
Reputation: 6090
The problem is that you're trying to setup the Single() extension method as part of your mocking. The setup call needs to have the result of your method -- not the result of your method with some extension method subsequently applied to it. I'd try this:
[TestMethod]
public void MyTestMethod()
{
var myMock = new Mock<IRepository<Person>>();
myMock.Setup(r => r.Find(It.IsAny<Expression<Func<Person, bool>>>())).Returns(new List<Person>() { new Person() }.AsQueryable());
Assert.IsTrue(true);
}
Here, you're just stubbing your Find() method with setup, and doing all of the other stuff in the Returns() clause. I'd suggest that approach in general. Setup should mirror your mocked item exactly, and you can do a bunch of black magic for the Returns() (or Throws() or whatever) call to get it to do what you want.
(When I ran that code in VS, it passed, so it wasn't throwing an exception)
Upvotes: 22