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

C# 使用实体框架进行单元测试的最佳实践,c#,entity-framework,unit-testing,C#,Entity Framework,Unit Testing,编辑:我忘了提到我使用的是代码优先的方法 我目前正在开发一个使用实体框架的应用程序。该系统目前拥有餐厅(在我的代码中称为主题)、用户和评论 我是使用实体框架的新手,所以我想知道如何正确设置单元测试。我假设我不必测试任何来自EF的插入、检索和修改数据,因为这可能已经测试过了。当我想为我正在编写的代码设置测试时,我应该如何处理来自EF的对象 我将给出一个我开发的单元测试的示例。我正在使用存储库结构来调用数据库。我知道这是没有必要的,但我喜欢在切换数据库时的抽象 我的存储库看起来像这样。(用户存储库基

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

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

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

我将给出一个我开发的单元测试的示例。我正在使用存储库结构来调用数据库。我知道这是没有必要的,但我喜欢在切换数据库时的抽象

我的存储库看起来像这样。(用户存储库基本上遵循相同的想法)

这是我的测试课

[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

我会避免在测试中使用正确的带有直接数据库调用的RecommenderContext。用存储库中的接口替换它,并创建一个可以注入存储库进行测试的伪实现。这样,您就不必担心在测试中留下残留物或连接到db


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

从纯粹的角度来看,您提供的示例代码中没有单元测试,因为测试取决于处于特定状态的基础数据库。为了隔离数据,您需要使用自己的存储库和datacontext的测试实现来伪造或模拟数据。您没有指定正在使用的EF的版本,但从EF 6开始,通过在
DbContext
上设置虚拟
DbSet
属性,可以通过模拟实现来覆盖,这一点变得更加容易。MSDN有一个代码示例,结合Moq as模拟框架解释了这一点。这篇文章中还有一个关于EF6之前解决方案的链接

话虽如此,我经常发现在单元测试中进行这种清晰的分离太麻烦了。我采用了您似乎也采用的方法,生成随机实体并将其插入测试数据库。不使用内存内测试加倍的另一个原因是,您使用的是Linq to对象,而不是Linq to SQL,这两者有细微的区别,尤其是在加载相关数据时


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

是的,我使用的是codefirst,忘了提及!因此,我不必使用我的上下文类,而是必须使用FakeDbset创建一个新类来用于测试?或者我可以将这些伪数据库集添加到现有上下文中吗?另外,避免使用直接数据库调用的RecommenderContext意味着什么?你能详细说明一下吗?是的,就是这样。您可以创建一个IReCommitderContext接口。然后是一个新的FakeRecommenderContext实现。因此,您不依赖调用数据库的测试,而是依赖内存中的伪上下文。这将使您的测试与在测试中直接调用数据库可能带来的各种问题隔离开来。我的意思是避免使用recommendercontext,它将在测试中直接调用数据库。当然,你可以在应用程序中以平常的方式使用它。这仅仅是为了单元测试,将自己与外部依赖(如数据库)隔离开来通常是很好的。好吧,这很有意义,谢谢!这可能是一个奇怪的问题,但您能否提供一个关于如何使用假数据集的示例代码?我是EF的新手,所以我不知道如何使用这些假货。这真的是假的吗?老实说,自从我上次这么做已经有一段时间了,我想我用了类似。。。
[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);
    }
}