0

I am trying to follow the guidelines provided http://msdn.microsoft.com/en-us/library/dn314429.aspx by Microsoft for Unittesting DbSets. All was going well - as they documented. Until I got to some code which works with a inheritance table. Since OfType() is an extension method, I cannot figure out how to create a Mock which will work to keep my code testable.

To clarify: I am trying to Test My Service Layer, which take a DBContext which is Injected, and which exposes several DbSets. In particular, I have an abstract History class, which has concrete derived types of StaffHistory, ContactHistory, etc. As a result, I only have 1 DbSet on my Dbcontext, which is of type History. I then use the Extension method OfType to set the discriminator and query the particular type.

When I create a Mock DbSet all usually works fine, except the OfType extension method fails, reporting NullReference Exception.

Any ideas or tips?

Service Layer:

public IEnumerable<ContactHistory> GetContactHistory(int ContactId, int AgeInDays)
{
    var age = DateTimeOffset.Now.AddDays(-Math.Abs(AgeInDays));
    return context.History.OfType<ContactHistory>()
        .Where(h => h.ContactId == ContactId && h.CreatedAt >= age)
        .AsEnumerable();
}

Unit Test Code:

[TestMethod]
public void History_Returns_Limited_Results()
{
    var testData = new List<ContactHistory> {
        new ContactHistory {
            ContactId = 1,
            CreatedAt = DateTimeOffset.Now,
            UserName = "UserA",
            Action = "Action",
        },
        new ContactHistory {
            ContactId = 4,
            CreatedAt = DateTimeOffset.Now.AddDays(-61),
            UserName = "UserA",
            Action = "Action",
        },
        new ContactHistory {
            ContactId = 4,
            CreatedAt = DateTimeOffset.Now.AddDays(-60),
            UserName = "UserA",
            Action = "Action",
        },
        new ContactHistory {
            ContactId = 4,
            CreatedAt = DateTimeOffset.Now,
            UserName = "UserA",
            Action = "Action",
        }
    }.AsQueryable();

    // Setup
    var mockContext = new Mock<IPEContext>();
    var mockSet = new Mock<IDbSet<History>>();
    mockSet.As<IQueryable<ContactHistory>>().Setup(m => m.Provider).Returns(testData.Provider);
    mockSet.As<IQueryable<ContactHistory>>().Setup(m => m.Expression).Returns(testData.Expression);
    mockSet.As<IQueryable<ContactHistory>>().Setup(m => m.ElementType).Returns(testData.ElementType);
    mockSet.As<IQueryable<ContactHistory>>().Setup(m => m.GetEnumerator()).Returns(testData.GetEnumerator());

    mockContext.Setup(c => c.History).Returns(mockSet.Object);

    // Test
    var service = new HistoryService(mockContext.Object);
    var historyFound = service.GetContactHistory(4, 60);

    // Verify
    Assert.IsNotNull(historyFound);
    Assert.AreEqual(2, historyFound.Count());
}

Is there something flawed in my approach? Is there something flawed in how I have setup my mock? This was following the Microsoft Article I mentioned above so that I could test service logic acting on a DbSet. The only flaw seems to be the Extension Method - not sure how I should work around that.

Ryan Posener
  • 191
  • 2
  • 10
  • You shouldn't have to mock `OfType()`, you should set up data in such a way that the code should work as expected. – AD.Net Jul 29 '14 at 14:15
  • can you provide an example? Here is the service method I am trying to build a tests for: `public IEnumerable GetClientHistory(Client Client, int AgeInDays) { var age = DateTimeOffset.Now.AddDays(-Math.Abs(AgeInDays)); return context.History.OfType() .Where(h => h.ClientId == Client.ClientId && h.CreatedAt >= age) .AsEnumerable(); }` – Ryan Posener Jul 29 '14 at 15:41
  • you should pass the `Client` object with populating the `History` property properly. I'm guessing `History` is a collection, so you should have some `ClientHistory` type objects there and some other non-matching ones. – AD.Net Jul 29 '14 at 16:18
  • Please re-read my question and if you can explain that last comment so that it makes sense, I would appreciate it. I'm not wanting to Fill an Attached Entity's ICollection property as I think you are suggesting - I'm wanting to perform a separate unique query. – Ryan Posener Jul 29 '14 at 20:41

1 Answers1

0

OK - I have figured this out. Of course there was a simple answer, but one which eluded me, because I had already mapped the Linq Provider and all in as the Type IQueryable. If you are using the .OfType() method, your mock must return on the Untyped Queryable method.

Here is the test code to allow the Method to work properly:

    [TestMethod]
    public void History_Returns_Limited_Results()
    {
        var today = new DateTimeOffset(DateTime.Today, DateTimeOffset.Now.Offset);
        var testData = new List<ContactHistory> {
            new ContactHistory {
                ContactId = 1,
                CreatedAt = today,
                UserName = "UserA",
                Action = "Action",
            },
            new ContactHistory {
                ContactId = 4,
                CreatedAt = today.AddDays(-61),
                UserName = "UserA",
                Action = "Action",
            },
            new ContactHistory {
                ContactId = 4,
                CreatedAt = today.AddDays(-60),
                UserName = "UserA",
                Action = "Action",
            },
            new ContactHistory {
                ContactId = 4,
                CreatedAt = today,
                UserName = "UserA",
                Action = "Action",
            }
        }.AsQueryable();

        // Setup
        var mockContext = new Mock<IPEContext>();
        var mockSet = new Mock<IDbSet<History>>();
        mockSet.As<IQueryable>().Setup(m => m.Provider).Returns(testData.Provider);
        mockSet.As<IQueryable>().Setup(m => m.Expression).Returns(testData.Expression);
        mockSet.As<IQueryable>().Setup(m => m.ElementType).Returns(testData.ElementType);
        mockSet.As<IQueryable>().Setup(m => m.GetEnumerator()).Returns(testData.GetEnumerator());
        mockContext.Setup(c => c.History).Returns(mockSet.Object);

        // Test
        var service = new HistoryService(mockContext.Object);
        var contact = new Person { ContactId = 4 };
        var historyFound = service.GetContactHistory(contact, 60);

        // Verify
        Assert.IsNotNull(historyFound);
        Assert.AreEqual(2, historyFound.Count());
    }
Ryan Posener
  • 191
  • 2
  • 10