使用实体框架进行单元测试的最佳实践

本文关键字:最佳 单元测试 实体 框架 | 更新日期: 2023-09-27 18:14:43

编辑:我忘了说我使用的是代码优先的方法

我目前正在开发一个使用实体框架的应用程序。该系统目前有餐厅(在我的代码中称为主题)、用户和评论。

我是全新的使用实体框架,所以我想知道如何正确设置单元测试。我假设我不需要测试来自EF的任何插入、检索和修改数据,因为这可能已经测试过了。当我想为我正在编写的代码设置测试时,我应该如何处理来自EF的对象?

我将给出一个我开发的单元测试的例子。我使用存储库结构来调用数据库。我知道这不是必要的,但我喜欢这种抽象,以防我们切换数据库。

我的存储库是这样的。(用户存储库基本上遵循相同的思想)

public class SubjectRepository
{
    private readonly RecommenderContext _context;
    public SubjectRepository()
    {
        _context = new RecommenderContext();
    }
    public Subject FindByGuid(Guid subjectId)
    {
        return _context.Subjects.Where(x => x.Id == subjectId).FirstOrDefault();
    }
    public void AddFollower(User userFollowing, Subject subjectToFollow)
    {
        _context.Subjects.Where(x => x.Id == subjectToFollow.Id).FirstOrDefault().Followers.Add(userFollowing);
        _context.SaveChanges();
    }
    public void CreateSubject(Subject sub)
    {
        _context.Subjects.Add(sub);
        _context.SaveChanges();
    }
}
这是我的测试类
[TestClass]
public class SubjectRepositoryTests
{
    SubjectRepository _subjectRepository;
    UserRepository _userRepository;
    [TestInitialize]
    public void init()
    {
        _subjectRepository = new SubjectRepository();
        _userRepository = new UserRepository();
    }
    [TestMethod]
    public void AddFollower()
    {
        Subject restaurant = Subject.CreateRandomSubject(); //These functions return randomized objects used for testing
        User activeUser = User.CreateRandomUser();
        _subjectRepository.CreateSubject(restaurant);
        _userRepository.CreateUser(activeUser);
        _subjectRepository.AddFollower(activeUser, restaurant);
        Assert.AreEqual(activeUser.Id, _subjectRepository.FindByGuid(restaurant.Id).Followers[0].Id);
    }
} 

这是设置单元测试的正确方法吗?如果是这样,我该如何正确地清理插入的对象呢?

关于如何在我的场景中更好地使用单元测试的任何其他反馈将非常感谢!

使用实体框架进行单元测试的最佳实践

假设您使用codefirst…

我会避免在测试中使用带有直接数据库调用的适当的推荐人上下文。将其替换为存储库中的接口,并创建一个可以注入到存储库中进行测试的伪实现。然后,您不必担心在测试中留下残留物或连接到db。

您可以使用FakeDbSet类之一来实现测试上下文。这将有效地测试您的存储库逻辑,同时忽略内置EF行为,并使您的测试很好地隔离。

从纯粹主义者的角度来看,您提供的示例代码中不是单元测试,因为测试依赖于处于特定状态的底层数据库。为了隔离,您需要通过使用您自己的存储库和数据上下文的测试实现来伪造或模拟您的数据。您没有指定您正在使用的EF版本,但是从EF 6开始,通过在DbContext上具有虚拟DbSet属性可以通过模拟实现覆盖,这使得这变得更容易。MSDN有一篇文章,其中包含代码示例,将其与Moq作为mock框架结合起来进行解释。在那篇文章中还有一个ef6之前的解决方案的链接。

话虽如此,我经常发现在单元测试中有这种清晰的分离太麻烦了。我采取的方法,你似乎已经采取了以及通过生成随机实体和插入那些在测试数据库。在内存测试中不使用double的另一个原因是你使用的是linq -to-object而不是Linq-to-SQL,两者有细微的区别,尤其是在加载相关数据时。

要清理测试数据,您可以使用您正在使用的测试框架的setup和teardown方法。