9

I'm trying to use Moq to make some tests for Entity Framework Code First classes. I'm very new to Moq and mocking techniques and I wonder if it's possible to easily do a test that I will describe below. I searched through the web for some solutions, but most are based on repository pattern, which I want to avoid.

I have ITestEntities interface for context

public interface ITestEntities
{
    IDbSet<Order> Orders { get; }
    IDbSet<Product> Products { get; }
    IDbSet<User> Users { get; }
}

Then context

public class TestEntities : DbContext, ITestEntities
{
    public TestEntities() : base("name=TestEntities")
    {

    }

    public virtual IDbSet<Order> Orders { get; set; }
    public virtual IDbSet<Product> Products { get; set; }
    public virtual IDbSet<User> Users { get; set; }
}

A controller and an action to test

public class HomeController : Controller
{
    private ITestEntities db;

    public HomeController()
    {
        db = new TestEntities();
    }

    public HomeController(ITestEntities db)
    {
        this.db = db;
    }

    public ActionResult Index()
    {
        var count = db.Users.Count();
        ViewBag.count = count;

        return View(count);
    }        
}

And finally a NUnit test using Moq

[Test]
public void ModelValueShouldBeTwo()
{
    var mockUsers = new Mock<IDbSet<User>>();
    mockUsers.Setup(m => m.Count()).Returns(2);

    var mockDB = new Mock<ITestEntities>();
    mockDB.Setup(db => db.Users).Returns((IDbSet<User>)mockUsers);

    var controller = new HomeController((ITestEntities)mockDB);

    var view = controller.Index();

    Assert.IsInstanceOf<ViewResult>(view);
    Assert.AreEqual(((ViewResult)view).Model, 2);
}

The problem is with this line: mockUsers.Setup(m => m.Count()).Returns(2);. When running this test I get following error:

System.NotSupportedException : Expression references a method that does not belong to the mocked object: m => m.Count<User>()

I think this is due to .Count() being a static method so it cannot be mocked by Moq. Is there a way to test this simple action using Moq and not using full-fledged repository pattern, which as I understand should anyway have this .Count() part hardcoded into some method to be testable... Maybe I just use the mocks in a wrong way? Because I have impression that this should be quite simple and possible with EF Code First.

2 Answers 2

16

If you are mocking the test entities you don't need to mock any further down the chain

Something like this should do (although, I 'm not at an IDE so may need some tweaking)

Update to include new InMemoryDbSet

[Test]
public void ModelValueShouldBeTwo()
{
    //Build test users
    var mockUsers = new InMemoryDbSet<User>(){ new User(), new User()};
    var mockDB = new Mock<ITestEntities>();
    //Set up mock entities to returntest users.
    mockDB.Setup(db => db.Users).Returns(mockUsers);

    var controller = new HomeController((ITestEntities)mockDB);

    var view = controller.Index();

    Assert.IsInstanceOf<ViewResult>(view);
    Assert.AreEqual(((ViewResult)view).Model, 2);
}

This will mean the extension methods will simply work off of the test data you have supplied.

See below for a good article on mocking dbset http://geekswithblogs.net/Aligned/archive/2012/12/12/mocking-or-faking-dbset.aspx

1
1

Mock GetEnumerator() instead of Count()

Count() is an extension method on objects that implement IEnumerable<T>, and IDbSet<T> implements IEnumerable<T>

Extension methods are passed the object that they are called on. In this case the signature is:

public static int Count<TSource>(
    this IEnumerable<TSource> source,  //This is your IDbSet that you are mocking
    Func<TSource, bool> predicate
)

Rather than trying to setup Count() to return a specific value, you can setup the members of IEnumerable<T> to achieve the same result. In the case of IEnumerable<T> all you have to do is set up GetEnumerator() to return an Enumerator<T> that enumerates over two values.

In this situation I usually create that Enumerator<T> by creating a new List with a couple of items and calling GetEnumerator() on it:

mockUsers.Setup(m => m.GetEnumerator()).Returns(new List<Users> {
    new User(),
    new User()
}.GetEnumerator());

Now, of course this effectively tests the extension method Count() in addition to whatever you are trying to achieve with your test, while that is a pretty low risk when the extension method is a part of .NET, it is something to keep in mind if you are using and authoring extension methods of your own.

1
  • 1
    This works for some kinds of queries against an IDbSet, but not all of them, for example FirstOrDefault() which doesn't use the enumerable weirdly.
    – Jon Adams
    Commented Dec 4, 2014 at 3:06

Not the answer you're looking for? Browse other questions tagged or ask your own question.