How do I use Moq and DbFunctions in unit tests to prevent a NotSupportedException?

c# entity-framework moq unit-testing

Question

Currently, I'm attempting to perform some unit tests on an Entity Framework-powered query. On the live version, the query itself functions without any problems, but the unit tests consistently fail.

This has been reduced to only how I use DbFunctions. TruncateTime, however I am unaware of a workaround that would enable the unit tests to accurately reflect what is occurring on the live server.

Here is the approach I'm taking:

    public System.Data.DataTable GetLinkedUsers(int parentUserId)
    {
        var today = DateTime.Now.Date;

        var query = from up in DB.par_UserPlacement
                    where up.MentorId == mentorUserId
                        && DbFunctions.TruncateTime(today) >= DbFunctions.TruncateTime(up.StartDate)
                        && DbFunctions.TruncateTime(today) <= DbFunctions.TruncateTime(up.EndDate)
                    select new
                    {
                        up.UserPlacementId,
                        up.Users.UserId,
                        up.Users.FirstName,
                        up.Users.LastName,
                        up.Placements.PlacementId,
                        up.Placements.PlacementName,
                        up.StartDate,
                        up.EndDate,
                    };

        query = query.OrderBy(up => up.EndDate);

        return this.RunQueryToDataTable(query);
    }

The tests all pass if I delete out the lines that contain DbFunctions (except for the ones that are checking that only valid results for a given date are run).

Is there a way I could give you a mockup of DbFunctions. To use TruncateTime in these tests? In essence, it ought to just return Datetime. Date, but EF queries don't have access to that.

Edit: Here is the test that utilizes the date check and fails:

    [TestMethod]
    public void CanOnlyGetCurrentLinkedUsers()
    {
        var up = new List<par_UserPlacement>
        {
            this.UserPlacementFactory(1, 2, 1), // Create a user placement that is current
            this.UserPlacementFactory(1, 3, 2, false) // Create a user placement that is not current
        }.AsQueryable();

        var set = DLTestHelper.GetMockSet<par_UserPlacement>(up);

        var context = DLTestHelper.Context;
        context.Setup(c => c.par_UserPlacement).Returns(set.Object);

        var getter = DLTestHelper.New<LinqUserGetLinkedUsersForParentUser>(context.Object);

        var output = getter.GetLinkedUsers(1);

        var users = new List<User>();
        output.ProcessDataTable((DataRow row) => students.Add(new UserStudent(row)));

        Assert.AreEqual(1, users.Count);
        Assert.AreEqual(2, users[0].UserId);
    }

Edit 2: The test's message and debug trace are as follows:

Test Result: Failed

Message: Assert.AreEqual failed. Expected:<1>. Actual:<0>

Debug Trace: This function can only be invoked from LINQ to Entities

According to what I've read, this is because, although there is one on the live version (as it's querying a SQL server), there isn't a LINQ to Entities implementation of this method that could be utilized in this position for the Unit Test.

1
28
1/23/2018 5:34:54 PM

Accepted Answer

Thanks to everyone's assistance, I was able to find a solution that worked for me after researching the shims that Quick advised. I was able to fix these tests after introducing a bogus EntityFramework assembly by changing them to the following:

[TestMethod]
public void CanOnlyGetCurrentLinkedUsers()
{
    using (ShimsContext.Create())
    {
        System.Data.Entity.Fakes.ShimDbFunctions.TruncateTimeNullableOfDateTime =
            (DateTime? input) =>
            {
                return input.HasValue ? (DateTime?)input.Value.Date : null;
            };

        var up = new List<par_UserPlacement>
        {
            this.UserPlacementFactory(1, 2, 1), // Create a user placement that is current
            this.UserPlacementFactory(1, 3, 2, false) // Create a user placement that is not current
        }.AsQueryable();

        var set = DLTestHelper.GetMockSet<par_UserPlacement>(up);

        var context = DLTestHelper.Context;
        context.Setup(c => c.par_UserPlacement).Returns(set.Object);

        var getter = DLTestHelper.New<LinqUserGetLinkedUsersForParentUser>(context.Object);

        var output = getter.GetLinkedUsers(1);
    }

    var users = new List<User>();
    output.ProcessDataTable((DataRow row) => users.Add(new User(row)));

    Assert.AreEqual(1, users.Count);
    Assert.AreEqual(2, users[0].UserId);
}
16
1/31/2014 9:15:40 AM

Popular Answer

Although I'm a little late to the party, writing your own method that leverages the DbFunction attribute is a relatively straightforward remedy. So utilize that function as opposed to DbFunctions. TruncateTime.

[DbFunction("Edm", "TruncateTime")]
public static DateTime? TruncateTime(DateTime? dateValue)
{
    return dateValue?.Date;
}

When Linq to Entities is used, this function will perform the EDM TruncateTime method; otherwise, it will run the given code.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow