Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq.Mock<T> - how to set up a method that takes an expression

I am Mocking my repository interface and am not sure how to set up a method that takes an expression and returns an object? I am using Moq and NUnit.

Interface:

public interface IReadOnlyRepository : IDisposable
{
    IQueryable<T> All<T>() where T : class;
    T Single<T>(Expression<Func<T, bool>> expression) where T : class;
}

Test with IQueryable is already set up, but don't know how to set up the T Single:

private Moq.Mock<IReadOnlyRepository> _mockRepos;
private AdminController _controller;
[SetUp]
public void SetUp()
{
    var allPages = new List<Page>();
    for (var i = 0; i < 10; i++)
    {
        allPages.Add(new Page { Id = i, Title = "Page Title " + i, Slug = "Page-Title-" + i, Content = "Page " + i + " on page content." });
    }
    _mockRepos = new Moq.Mock<IReadOnlyRepository>();
    _mockRepos.Setup(x => x.All<Page>()).Returns(allPages.AsQueryable());
    //Not sure what to do here???
    _mockRepos.Setup(x => x.Single<Page>()
    //----
    _controller = new AdminController(_mockRepos.Object);
}
like image 990
Paul Avatar asked May 01 '10 23:05

Paul


People also ask

How do you call an original method in mock?

Set CallBase to true on your mock. This will call the original virtual methods or properties if they exist, and haven't been set up to return a canned value.

Can you mock a class with Moq?

You can use Moq to create mock objects that simulate or mimic a real object. Moq can be used to mock both classes and interfaces. However, there are a few limitations you should be aware of. The classes to be mocked can't be static or sealed, and the method being mocked should be marked as virtual.

What is CallBase in Moq?

CallBase , when initialized during a mock construction, is used to specify whether the base class virtual implementation will be invoked for mocked dependencies if no setup is matched. The default value is false .

How do you mock a function in C#?

Trying to mock a method that is called within another method. // code part public virtual bool hello(string name, int age) { string lastName = GetLastName(); } public virtual string GetLastName() { return "xxx"; } // unit test part Mock<Program> p = new Mock<Program>(); p. Setup(x => x. GetLastName()).


4 Answers

You can set it up like this:

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))//.Returns etc...; 

However you are coming up against one of Moq's shortcomings. You would want to put an actual expression there instead of using It.IsAny, but Moq doesn't support setting up methods that take expressions with specific expressions (it's a difficult feature to implement). The difficulty comes from having to figure out whether two expressions are equivalent.

So in your test you can pass in any Expression<Func<Page,bool>> and it will pass back whatever you have setup the mock to return. The value of the test is a little diluted.

like image 51
Jason Punyon Avatar answered Sep 28 '22 11:09

Jason Punyon


Have the .Returns call return the result of the expression against your allPages variable.

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))
    .Returns( (Expression<Func<Page, bool>> predicate) => allPages.Where(predicate) );
like image 33
wllmsaccnt Avatar answered Sep 28 '22 12:09

wllmsaccnt


I have found that It.Is<T> should be used in place of It.IsAny<T> for more accurate results.

Page expectedPage = new Page {Id = 12, Title = "Some Title"};
_mockRepos.Setup(x => x.Single<Page>(It.Is<Expression<Func<Page, bool>>>(u => u.Compile().Invoke(expectedPage))))
                       .Returns(() => expectedPage);
like image 40
Paul Avatar answered Sep 28 '22 12:09

Paul


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 an example of one possible end-result:

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/

like image 45
Byron Sommardahl Avatar answered Sep 28 '22 11:09

Byron Sommardahl