Mock ForeignKey列没有按预期工作

时间:2018-05-09 13:29:38

标签: c# entity-framework unit-testing testing mocking

我的实体如下:

List<Integer>

我有以下测试,我预计会失败,但会通过:

List<Number>

这是public class Class1 { public int Id { get; set; } public string Name { get; set; } public virtual Class2 Class2{ get; set; } } public class Class2 { [Key, ForeignKey("Class1")] public int Id { get; set; } public int? Price { get; set; } public virtual Class1 Class1{ get; set; } } 方法:

[TestMethod]
public void CreateClass2()
{
    var mockSet = new Mock<DbSet<Class2>>();

    var mockContext = new Mock<theContext>();
    mockContext.Setup(m => m.Class2s).Returns(mockSet.Object);

    var service = new Class2Service(mockContext.Object);
    service.AddClass2(10, 100);

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

由于我尚未添加service.AddClass2并为public class Class2Service { private readonly theContext _context; public CoordService(theContext context) { _context = context; } public Class2 AddClass2(int id, int? price) { var class2 = _context.Class2s.Add(new Class2 { Id = id, Price = price }); _context.SaveChanges(); return class2; } } 输入了无效的Class1(即id中不存在),{{1}的新条目1}}应该是无效的,因为我用外键约束它。 在我的项目和真实数据库中,它按预期工作并给我错误,但在此测试中它已通过!

1 个答案:

答案 0 :(得分:0)

测试使用模拟上下文而不是真实上下文,因此_context.SaveChanges();实际上什么也不做,因此不会抛出任何异常。

您可以轻松地谷歌如何正确模拟DbContext,例如herehere

此外,我认为单元测试本身应该以不同的方式编写。即你正在测试这里的方法调用,这是添加记录的底层实现。

针对此案例的正确单元测试将检查service.AddClass2(10, 100);调用是否抛出异常。应该编写一个单独的测试,以查看作为service.AddClass2方法调用的结果,DbSet中是否出现了新记录,而不是检查是否调用了某些特定方法。

UPD:将IQueriable设置为虚假上下文源的示例。

    public static class MockDbSetExtensions
{
    public static void SetSource<T>(this Mock<DbSet<T>> mockSet, IList<T> source) where T : class
    {
        var data = source.AsQueryable();
        var iQuryableSet = mockSet.As<IQueryable<T>>();

        iQuryableSet.SetupGet(m => m.Provider).Returns(data.Provider);
        iQuryableSet.SetupGet(m => m.Expression).Returns(data.Expression);
        iQuryableSet.SetupGet(m => m.ElementType).Returns(data.ElementType);
        iQuryableSet.Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
    }
}

样本用法:

    [TestClass]
public class GigRepositoryTests
{

    private GigRepository _repository;
    private Mock<DbSet<Gig>> _mockGigs;
    private Mock<IApplicationDbContext> mockContext;

    [TestInitialize]
    public void TestInitialize()
    {
        _mockGigs = new Mock<DbSet<Gig>>();

        mockContext = new Mock<IApplicationDbContext>();


        _repository = new GigRepository(mockContext.Object);
    }

    [TestMethod]
    public void GetUpcomingGigsByArtist_GigsInThePast_ShouldNotBeReturned()
    {
        var gig = new Gig() {DateTime = DateTime.Now.AddDays(-1), ArtistId = "1"};

        _mockGigs.SetSource(new List<Gig> {gig});
        mockContext.SetupGet(c => c.Gigs).Returns(_mockGigs.Object);

        var gigs = _repository.GetUpcomingGigs("1");

        gigs.Should().BeEmpty();
    } }