【问题标题】:Mock ForeignKey column is not working as per expectation模拟 ForeignKey 列未按预期工作
【发布时间】:2018-10-19 15:32:28
【问题描述】:

我的实体如下:

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 并且为Class2 输入了无效的id(即它在Class1 中不存在),Class2 的新条目应该是无效的,因为我已经限制它与外键。 在我的项目和真实数据库中,它按预期工作并给我错误,但在这个测试中它通过了!

【问题讨论】:

  • 我认为你的 ForeignKey("Class1") 应该是 ForeignKey("Class1Id")
  • @Steve 这个ForeignKey 行为在我的数据库中按预期工作,并且真正添加了 Class2,它只是在测试中不起作用。

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


【解决方案1】:

测试使用模拟上下文而不是真实上下文,所以_context.SaveChanges(); 实际上什么都不做,所以不会抛出异常。

您可以轻松地在 Google 上搜索如何正确模拟 DbContext,例如herehere

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

针对这种情况的适当单元测试将检查service.AddClass2(10, 100); 调用是否引发异常。并且应该编写一个单独的测试来查看 DbSet 中是否出现了新记录作为service.AddClass2 方法调用的结果,而不是检查是否调用了某些特定方法。

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

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

【讨论】:

  • 我已经完全使用了这个引用msdn.microsoft.com/en-us/library/dn314429(v=vs.113).aspx,但我不知道我应该如何模拟 DbContext 属性?此测试中使用的上下文也使用了Class2s,这是我原来的 DbContext 中使用的真实上下文。
  • 你能给我一个如何模拟 ForeignKey 列行为的例子吗?
  • 我更新了我的问题,表明我正在使用真实的上下文。
  • 你仍然没有测试真正的上下文,因为你在这里通过服务的构造函数传递了一个假的:` var service = new Class2Service(mockContext.Object);` 实际上这看起来是不可能的测试外键行为是无用的,因为它实际上是数据库的业务。请查看这个非常相似的问题的答案:stackoverflow.com/questions/6904139/…
  • 那我该怎么办?您能否在答案中附加解决方案?我真的很困惑,因为我真的是 Mocking 的新手
猜你喜欢
  • 2020-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-02
  • 1970-01-01
  • 1970-01-01
  • 2014-04-05
相关资源
最近更新 更多