繁体   English   中英

如何使用Moq对实体框架6中的删除操作进行单元测试

[英]How to use Moq to unit test a delete operation in entity framework 6

*更新编辑 - 部分解决方案 - 仍然需要帮助* - 我发现异常只是误导。 它给了我这个例外,因为我得到了被嘲笑的属性被错误调用的次数。 它应该被调用两次,而不是一次。 那部分现在有效。

但我仍然不明白为什么实体没有从列表中删除。 是因为它是可查询的吗?

原始问题如下

我一直在尝试关注此链接,以了解如何对Entity Framework 6和6.1进行单元化。

但是,它没有显示如何对删除操作进行单元测试 这是我试图测试的代码:

public void DeleteRequirement(int id)
{
    Requirement requirementToDelete = GetRequirement(id);
    context.Requirement.Remove(requirementToDelete);
    context.SaveChanges();
}

public Requirement GetRequirement(int id)
{
    return (from result in context.Requirement
            where result.Id == id
            select result).SingleOrDefault();
}

我的单元测试代码是

[TestMethod]
public void DeleteRequirementSuccessfully()
{
    var requirements = new List<Requirement>
    {
        new Requirement {
            Id = 1,
            Title = "Requirement 1",
            Description = "Requirement 1 description"
        },
        new Requirement {
            Id = 2,
            Title = "Requirement 2",
            Description = "Requirement 2 description"
        },
        new Requirement {
            Id = 3,
            Title = "Requirement 3",
            Description = "Requirement 3 description"
        }
    }
    .AsQueryable();

    var mockDbSet = new Mock<DbSet<Requirement>>();
    var context = new Mock<RequirementsDatabaseEntities>();

    mockDbSet.As<IQueryable<Requirement>>()
             .Setup(x => x.Provider)
             .Returns(requirements.Provider);
    mockDbSet.As<IQueryable<Requirement>>()
             .Setup(x => x.ElementType)
             .Returns(requirements.ElementType);
    mockDbSet.As<IQueryable<Requirement>>()
             .Setup(x => x.Expression)
             .Returns(requirements.Expression);
    mockDbSet.As<IQueryable<Requirement>>()
             .Setup(x => x.GetEnumerator())
             .Returns(requirements.GetEnumerator());

    context.Setup(x => x.Requirement).Returns(mockDbSet.Object);

    var dataAccess = new RequirementsDataAccess(context.Object);
    int idToDelete = 1;
    dataAccess.DeleteRequirement(idToDelete);

    context.VerifyGet(x => x.Requirement, Times.Exactly(2)); // <- now verification is correct
    mockDbSet.Verify(x => x.Remove(It.IsAny<Requirement>()), Times.Once());
    context.Verify(x => x.SaveChanges(), Times.Once());
}

context.VerifyGet语句的测试失败,出现以下错误

Test method DataAccessTest.RequirementUnitTest+DeleteRequirement.DeleteRequirementSuccessfully threw exception:
System.InvalidOperationException: No connection string named
    'RequirementsDatabaseEntities' could be found in the application config file.

如果我注释掉the context.VerifyGet行测试通过,但是不会从列表中删除该要求。 有谁知道为什么?

  1. 测试失败
  2. 为什么当我注意到违规行时,它会通过,但要求尚未删除。

为什么这不起作用?

首先将requirements定义编辑为List<Requirement>而不是Queryable ,以便能够模拟添加或删除。 并在Setup方法中使用requirements.AsQueryable()

第二步将此代码添加到模拟删除:

mockDbSet.Setup(m => m.Remove(It.IsAny<Requirement>())).Callback<Requirement>((entity) => requirements.Remove(entity));

因此,您可以在删除后检查requirements清单的计数。 你的代码应该是这样的:

[TestMethod]
public void DeleteRequirementSuccessfully()
{
    var requirements = new List<Requirement>
    {
        new Requirement {
            Id = 1,
            Title = "Requirement 1",
            Description = "Requirement 1 description"
        },
        new Requirement {
            Id = 2,
            Title = "Requirement 2",
            Description = "Requirement 2 description"
        },
        new Requirement {
            Id = 3,
            Title = "Requirement 3",
            Description = "Requirement 3 description"
        }
    };

    var mockDbSet = new Mock<DbSet<Requirement>>();
    var context = new Mock<RequirementsDatabaseEntities>();

    // You should use .AsQueryable() in these lines
    mockDbSet.As<IQueryable<Requirement>>()
             .Setup(x => x.Provider)
             .Returns(requirements.AsQueryable().Provider);
    mockDbSet.As<IQueryable<Requirement>>()
             .Setup(x => x.ElementType)
             .Returns(requirements.AsQueryable().ElementType);
    mockDbSet.As<IQueryable<Requirement>>()
             .Setup(x => x.Expression)
             .Returns(requirements.AsQueryable().Expression);
    mockDbSet.As<IQueryable<Requirement>>()
             .Setup(x => x.GetEnumerator())
             .Returns(requirements.GetEnumerator());

    // This line should be added
    mockDbSet.Setup(m => m.Remove(It.IsAny<Requirement>())).Callback<Requirement>((entity) => requirements.Remove(entity));

    context.Setup(x => x.Requirement).Returns(mockDbSet.Object);

    var dataAccess = new RequirementsDataAccess(context.Object);
    int idToDelete = 1;
    dataAccess.DeleteRequirement(idToDelete);

    context.VerifyGet(x => x.Requirement, Times.Exactly(2));
    //mockDbSet.Verify(x => x.Remove(It.IsAny<Requirement>()), Times.Once());
    context.Verify(x => x.SaveChanges(), Times.Once());

    // add this Assert
    Assert.AreEqual(requirement.Count, 2);
    // or
    Assert.IsFalse(requirement.Any(x => x.Id == idToDelete));
}
  1. 它失败了因为你不能模拟非虚方法。
  2. 同样的问题: RequirementsDatabaseEntities.Requirement不是虚拟方法,而是在测试方法中提供的输出比预期的要多。 它可能会返回空集合。

修复:make RequirementsDatabaseEntities.Requirement getter virtual

部分解决方案 -我发现异常只是误导。 它给了我这个例外,因为我得到了被嘲笑的属性被错误调用的次数。 它应该被调用两次,而不是一次。 那部分现在有效。 但我仍然不明白为什么实体没有从列表中删除。 是因为它是可查询的吗?

由于Moq使用继承来替换方法调用,因此您只能模拟虚拟方法(或接口)。

因此,要么使用虚拟虚拟方法/属性,要么使用可以使用Jit编织工作的Isolator / JustMock等,并伪造这些方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM