26

I have the following classes (where PilsnerContext is a DbContext class):

public abstract class ServiceBase<T> : IService<T> where T: class, IEntity
{
    protected readonly PilsnerContext Context;

    protected ServiceBase(PilsnerContext context)
    {
        Context = context;
    }

    public virtual T Add(T entity)
    {
        var newEntity = Context.Set<T>().Add(entity);
        Context.SaveChanges();
        return newEntity;
    }
}

public class ProspectsService : ServiceBase<Prospect>
{
    public ProspectsService(PilsnerContext context) : base(context){}

}

And i'm trying to make a unit test of the Add method mocking the context like:

[TestClass]
public class ProspectTest
{
    [TestMethod]
    public void AddProspect()
    {
        var mockProspect = new Mock<DbSet<Prospect>>();
        var mockContext = new Mock<PilsnerContext>();

        mockContext.Setup(m => m.Prospects).Returns(mockProspect.Object);

        var prospectService = new ProspectsService(mockContext.Object);

        var newProspect = new Prospect()
        {
            CreatedOn = DateTimeOffset.Now,
            Browser = "IE",
            Number = "1234567890",
            Visits = 0,
            LastVisitedOn = DateTimeOffset.Now
        };

        prospectService.Add(newProspect);

        mockProspect.Verify(m=>m.Add(It.IsAny<Prospect>()), Times.Once);
        mockContext.Verify(m=>m.SaveChanges(), Times.Once);
    }
}

But the assert:

mockProspect.Verify(m=>m.Add(It.IsAny<Prospect>()), Times.Once);

Is failing, I assume is because I'm using Context.set().Add() instead of Context.Prospects.Add() in the Add method but how is the correct way to pass this test?

The exception is:

Expected invocation on the mock once, but was 0 times: m => m.Add(It.IsAny<Prospect>()) No setups configured. No invocations performed.

Thanks in advance.

ferflores
  • 1,084
  • 3
  • 18
  • 35

2 Answers2

22

It looks like you're just missing the setup to return your DbSet:

mockContext.Setup(m => m.Set<Prospect>()).Returns(mockProspect.Object);
Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
  • 1
    `DbSet.Set()` is a non virtual method. – haim770 Jun 24 '14 at 20:23
  • [The docs](http://msdn.microsoft.com/en-us/library/gg696521%28v=vs.113%29.aspx) say it is, unless I'm looking at the wrong method. I'm not an EF user, so I could be completely wrong. – Patrick Quirk Jun 24 '14 at 20:37
  • 4
    Apparently the method was made virtual in [this commit](http://entityframework.codeplex.com/SourceControl/changeset/6dc8879b13f54382eb087dd010636bebc15fdc69), which is in EF 6.1.0 and later. Just making sure I'm not crazy! – Patrick Quirk Jun 25 '14 at 02:06
  • 1
    most example on msdn is with service and direcly using context; Without this line, generic repository cannot test! Thanks... – ttacompu Apr 01 '15 at 15:34
  • In my opinion this should be the answer to the question – theLaw Jan 16 '18 at 18:04
5

I tried your solution Patrick Quirk but I was getting an error telling me that DbContext.Set is not virtual.

I found the solution for that here:

How to mock Entity Framework 6 Async methods?

Creating an interface of the DbContext like

    public interface IPilsnerContext
   {
       DbSet<T> Set<T>() where T : class;
   }

That way I could mock it.

Thanks!

This is my first question btw, I'm not sure if I can mark this question as duplicated or something.

Community
  • 1
  • 1
ferflores
  • 1,084
  • 3
  • 18
  • 35
  • That question is pretty close, I'll flag your question as a possible duplicate. Yours is a subset of the question you link to, so I think that qualifies. – Patrick Quirk Jun 25 '14 at 01:53