UnitTesting EF6 with OfType <t> </t>

时间:2014-07-29 14:08:41

标签: entity-framework unit-testing extension-methods dbset

我正在尝试遵循Microsoft为单元测试DbSets提供的http://msdn.microsoft.com/en-us/library/dn314429.aspx指南。一切顺利 - 正如他们记录的那样。直到我得到一些与继承表一起使用的代码。由于OfType()是一个扩展方法,我无法弄清楚如何创建一个Mock,它将使我的代码可以测试。

澄清一下:我正在尝试测试我的服务层,它采用了一个注入的DBContext,并暴露了几个DbSet。特别是,我有一个抽象的History类,它具有StaffHistory,ContactHistory等的具体派生类型。因此,我的Dbcontext上只有1个DbSet,它的类型为History。然后,我使用Extension方法OfType来设置鉴别器并查询特定类型。

当我创建Mock DbSet时,除了OfType扩展方法失败之外,通常都能正常工作,报告NullReference异常。

任何想法或提示?

服务层:

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();
}

单元测试代码:

[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());
}

我的做法有什么缺陷吗?我如何设置我的模拟有什么缺陷吗?这是遵循上面提到的Microsoft文章,以便我可以测试作用于DbSet的服务逻辑。唯一的缺陷似乎是扩展方法 - 不知道我应该如何解决这个问题。

1 个答案:

答案 0 :(得分:0)

好的 - 我已经弄明白了。当然有一个简单的答案,但有一个让我望而却步,因为我已经将Linq Provider和所有的映射都作为Type IQueryable映射。如果您使用.OfType()方法,则模拟必须返回Untyped Queryable方法。

以下是允许方法正常工作的测试代码:

    [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());
    }