This is a migrated thread and some comments may be shown as answers.

JustMock LINQ types “Func<>” and “Expression<>”

11 Answers 185 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Carlos
Top achievements
Rank 1
Carlos asked on 08 Apr 2011, 07:20 PM

Hi, I'm trying to unit test a common interface used in a generic Repository, something like T GetWhere(Func<T, Boolean> where)...
When I set this expectation in a test

Mock.Arrange(() => bookRepo.GetWhere(book => book.Id == 1))
                 .Returns(expectedBook)
                 .MustBeCalled();

JustMock throws this exception:  "Telerik.JustMock.MockAssertionException: Setup contains calls that are marked as "MustBeCalled" but actually never called"
It seems the GetWhere() method was never call, to return the expected object.
Below is a simple example that illustrate the case.  
How should I rewrite the second test below so that I can mock and test these type of interfaces? 
Thanks in advance. 

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Telerik.JustMock; //Free version 2011.1.331.1
using Model.Infrastructure;
using Model;
  
namespace Model.Infrastructure
{
    public interface IRepository<T> where T : class
    {
        T GetById(int Id);
        T GetWhere(Func<T, Boolean> where);
    }
}
  
namespace Model
{
    public class Book
    {
        public int Id { get; set; }
        public string BookTitle { get; set; }
        public string OnLoanTo { get; set; }
        public Book(){}
    }
  
    public class BookService
    {
        private IRepository<Book> _bookRepository;
        public BookService(IRepository<Book> bookRepository)
        {
            this._bookRepository = bookRepository;
        }
  
        public Book GetSingleBook1(int bookId)
        {
            return _bookRepository.GetById(bookId);;
        }
  
        public Book GetSingleBook2(int bookId)
        {
            Book b = _bookRepository.GetWhere(x => x.Id == bookId);
            return b;
        }
    }
}
  
namespace Model.Tests
{
    [TestClass]
    public class AssortedTests
    {
        private Book expectedBook;
        private IRepository<Book> bookRepo;
        private BookService bookService;
  
        [TestInitialize]
        public void Setup()
        {
            expectedBook = new Book()
            { Id = 1,
              BookTitle =
"Adventures",
              OnLoanTo =
"John Smith"
            };
            bookRepo = Mock.Create<IRepository<Book>>();
            bookService = new BookService(bookRepo);
        }
  
        [TestMethod]
        public void Book_CanRetrieveBookFromRepository()
        {
            Mock.Arrange(() => bookRepo.GetById(1))
                             .Returns(expectedBook)
                             .MustBeCalled();
            //Act
            Book actualBook = bookService.GetSingleBook1(1); 
  
            Mock.Assert(bookRepo); //Pass...
            Assert.IsTrue(actualBook.BookTitle == "Adventures");
        }
  
        [TestMethod]
        public void Book_CanRetrieverFromRepositoryUsingLambdaExpressions()
        {
            Mock.Arrange(() => bookRepo.GetWhere(book => book.Id == 1))
                             .Returns(expectedBook)
                             .MustBeCalled();
            //Act
            Book actualBook = bookService.GetSingleBook2(1);
  
            Mock.Assert(bookRepo); //Throws the error...
            Assert.IsTrue(actualBook.BookTitle == "Adventures");
        }
    }
}

11 Answers, 1 is accepted

Sort by
0
Ricky
Telerik team
answered on 12 Apr 2011, 03:30 PM
Hello Carlos,

Thanks again for sending the issue. However, you could write the failing test in the following way that will make it work:

[TestMethod]
public void Book_CanRetrieverFromRepositoryUsingLambdaExpressions() //Fail...
{
    Mock.Arrange(() => bookRepo.GetWhere(Arg.IsAny<Func<Book, Boolean>>()))
                     .Returns(expectedBook)
                     .MustBeCalled();
    //Act
    Book actualBook = bookService.GetSingleBook2(1);
 
    Mock.Assert(bookRepo);
    Assert.IsTrue(actualBook.BookTitle == "Adventures");
}

But your report is legitimate and seems to be the case for other popular tools as well. Like the one I found in this stackoverflow thread:

http://stackoverflow.com/questions/5196669/moqing-methods-where-expressionfunct-bool-are-passed-in-as-parameters

But we would love to implement this as a feature and thanks again for pointing this to us. You can further follow the task in the following PITS entry:

http://www.telerik.com/support/pits.aspx#/public/justmock/5515


Kind Regards,
Mehfuz
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Carlos
Top achievements
Rank 1
answered on 12 Apr 2011, 11:12 PM
Thank you for the feedback Mehfuz.
I found the test also pass if "IgnoreArgument()" is used instead of your proposed workaround:Arg.IsAny<Func<Book, Boolean>>().  In either case, I feel the test is not quite-right as it is ignoring the argument anyway. 

[TestMethod]
public void Book_CanRetrieverFromRepositoryUsingLambdaExpressions()
{
    Mock.Arrange(() => bookRepo.GetWhere(b => b.Id == 1))
                     .IgnoreArguments()//Now it pass, but accepts any argument!
                     .Returns(expectedBook)
                     .MustBeCalled();
    //Act
    Book actualBook = bookService.GetSingleBook2(Arg.IsAny<int>());
 
    Mock.Assert(bookRepo);
    Assert.IsTrue(actualBook.BookTitle == "Adventures");
}

I'm glad this Mocking methods where Expression<Func<T, bool>> are passed in as parameters is going to be implemented as a feature for the product.  Great!
0
Ricky
Telerik team
answered on 13 Apr 2011, 09:01 AM
Hello Carlos,

Thanks again for your reply. Yes you are right that you can use IgnoreArguments as well instead of Arg.IsAny<Func<Book, Boolean>>(). Sorry that i forgot to mention it. Also, its great that you pointed this Mocking methods where Expression<Func<T, bool>> are passed in as parameters out.

Should you have any more issues , please don't hesitate to write us back.

Kind regards,
Mehfuz
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Josh
Top achievements
Rank 1
answered on 17 Jan 2013, 06:31 AM
I apologize for bringing up an old issue, but is there a way to use Arg.Matches on an Expression/Func?  I could not find any other posts that refer to matching on an Expression for an Expression.  We have several cases where I would like to assert that a particular query is run against the database or arrange a query to return a specific result set.

Thanks!
0
Ricky
Telerik team
answered on 17 Jan 2013, 11:56 PM
Hi Josh,

Thanks for bringing up the issue.

The original bug that was mentioned in PITS is fixed now:

http://www.telerik.com/support/pits.aspx#/public/justmock/5515

Since the argument is a query expression itself, It will be redundant to wrap it around a Arg.Matcher as you can always do this:

Mock.Arrange(() => repository.GetWhere(book => book.Id == 1)).Returns(expected).MustBeCalled();

Hope this answers your question.

 

Kind Regards
Mehfuz
the Telerik team
Share what you think about JustTrace & JustMock with us, so we can become even better! You can use the built-in feedback tool inside JustTrace, our forums, or our JustTrace or JustMock portals.
0
Josh
Top achievements
Rank 1
answered on 18 Jan 2013, 01:39 PM
I saw that in the documentation, but I'm still missing something:

Here is my test:
<code>
            var charge = new Charge();
            var charges = new[] {charge};
            Mock.Arrange(() => repo.Get<Charge>(c => c.OrgID == 1 && c.IsActive, Arg.IsAny<List<string>>())).Returns(charges).Occurs(1);
</code>

And here is my code under test:
<code>
            var list = genericRepository.Get<Charge>(c => c.OrgID == orgID && c.IsActive, new List<string> { "ChargeCode" });
</code>

My test fails when I assert my repo mock object saying it was called 0 times. Even if I change my code under test to use an explicit 1, the assertion fails.  I upgraded to the latest Nuget package for JustMock (lite).  Do you have any suggestions?

Thank you, again.
0
Ricky
Telerik team
answered on 23 Jan 2013, 04:43 PM
Hi Josh,

Thanks a lot for the snippet.

However, I would ask you to send us a sample project which will let us debug the issue further.

Here are few things that you might like to watch out:

  • Since you are using lite version make sure that you are not mocking a non-virtual method.
  • Wrap the expression argument in a variable and then pass it both for arrange/actual execution and please check that it works.

Kind Regards
Mehfuz
the Telerik team
Share what you think about JustTrace & JustMock with us, so we can become even better! You can use the built-in feedback tool inside JustTrace, our forums, or our JustTrace or JustMock portals.
0
Josh
Top achievements
Rank 1
answered on 30 Jan 2013, 01:32 PM
Hi Mehfuz,

Sorry for the delayed response.  I've whipped up a quick sample of my problem, but not sure how to get the ZIP to you. 
0
Kaloyan
Telerik team
answered on 30 Jan 2013, 01:55 PM
Hi Josh,

In order to send us the example, it will be best for you to open a new support ticket and to attach the .ZIP in your post.

Feel free to ask for further assistance.

Regards,
Kaloyan
the Telerik team
Share what you think about JustTrace & JustMock with us, so we can become even better! You can use the built-in feedback tool inside JustTrace, our forums, or our JustTrace or JustMock portals.
0
Josh
Top achievements
Rank 1
answered on 30 Jan 2013, 03:04 PM
OK, I don't have an active Telerik subscription, so I'm going to post code here:

Here is my class:

public class Process
{
    private IRepo repos;
 
    public Process(IRepo repo)
    {
        repos = repo;
    }
 
    public List<Payment> GetPayments(bool paid, int acctNo)
    {
        Expression<Func<Payment, bool>> func = p => p.IsPaid == paid && p.ToAccountId == acctNo;
        var result = repos.Get<Payment>(func);
        return result.ToList();
    }
}

And here is my test:
public class ProcessTest
{
    private IRepo mockRepo;
 
    [TestInitialize]
    public void Setup()
    {
        mockRepo = Mock.Create<IRepo>();
 
        var payment = new Payment() {IsPaid = false, ToAccountId = 1234};
        var list = new[] {payment};
 
        Expression<Func<Payment, bool>> func = p => p.IsPaid == false && p.ToAccountId == 1234;
        Mock.Arrange(() => mockRepo.Get<Payment>(func, null)).Returns(list).OccursOnce();
    }
 
    [TestMethod]
    public void TestMethod1()
    {
        var sut = new Process(mockRepo);
 
        var result = sut.GetPayments(false, 1234);
 
        Mock.Assert(mockRepo);
    }
}

This test fails and I don't know why.  

Does the Expression have to be the exact same object?
What if I want to use Arg.Matches() and just match on a part of the Expression?
0
Ricky
Telerik team
answered on 30 Jan 2013, 09:54 PM
Hi Carlos,

Thanks again for reaching out to us. 

Your test looks fine, however it’s an issue with AndAlso type expression processing in JustMock. Good news is that we fixed the issue and should you need a fix urgently then please open a support where I will send you the build.

On your second question, you don't need Arg.Matches since it’s a query parameter itself and using of Arg.Matches is redundant as your main goal here is to match a mocked method based on the expression you supplied in Mock.Arrange which you can write directly without the help of Arg.Matches


Kind Regards
Mehfuz
the Telerik team
Share what you think about JustTrace & JustMock with us, so we can become even better! You can use the built-in feedback tool inside JustTrace, our forums, or our JustTrace or JustMock portals.
Tags
General Discussions
Asked by
Carlos
Top achievements
Rank 1
Answers by
Ricky
Telerik team
Carlos
Top achievements
Rank 1
Josh
Top achievements
Rank 1
Kaloyan
Telerik team
Share this question
or