使用Entity Framework进行单元测试的最佳实践

时间:2016-10-25 10:14:45

标签: c# entity-framework unit-testing

编辑:我忘了提及我使用的是Code First方法

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

我是使用实体框架的新手,所以我想知道如何正确设置单元测试。我假设我不必测试任何插入,检索和修改来自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);
    }
} 

这是设置单元测试的正确方法吗?如果是这样,我将如何正确清理我插入的物体?

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

2 个答案:

答案 0 :(得分:2)

假设您正在使用codefirst ...

我会避免在测试中使用带有直接数据库调用的正确的RecommenderContext。将其替换为存储库中的接口,并创建一个可以注入存储库进行测试的虚假实现。那么你不必担心在测试中留下残留物或连接数据库。

您可以使用其中一个FakeDbSet类来实现测试上下文。实际上,这将测试您的存储库逻辑,同时忽略内置的EF行为,并使您的测试保持良好隔离。

答案 1 :(得分:0)

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

话虽如此,我经常发现在单元测试中明确分离太麻烦了。我采用你似乎采用的方法,生成随机实体并将其插入测试数据库中。不使用内存测试双精度的另一个原因是你使用的是Linq-to-objects而不是Linq-to-SQL,它们之间存在细微差别,尤其是在加载相关数据时。

要清理测试数据,您可以使用您正在使用的测试框架的设置和拆卸方法。