如何使用ADO.NET实体框架测试存储库模式?

Geo*_*Geo 9 c# tdd asp.net-mvc design-patterns repository-pattern

在使用Repository模式时,我发现很难理解使用TDD技术设计软件的原因,而实际上您必须在持久性数据集中实现存储库的接口.

为了明确我的观点,我将提交一个例子:

我的域模型上有以下界面:

public interface IUserRepository
{
    IQueryable<User> FindAllUsers();
    void AddUser(User newUser);
    User GetUserByID(int userID);
    void Update(User userToUpdate);
}
Run Code Online (Sandbox Code Playgroud)

我有以下用于测试目的的接口实现:

public class FakeUserRepository : IUserRepository
{
    private IList<User> _repository;

    public FakeUserRepository()
    {
        _repository = new List<User>();
        ... //create all users for testing purposes

    }

    public IQueryable<User> FindAllUsers()
    {
        return _repository.AsQueryable<User>(); //returns all users
    }
Run Code Online (Sandbox Code Playgroud)

现在我创建一些测试:

  1. Can_Add_User
  2. Can_Add_Account用户
  3. Can_Add_ShareSpace用于具有其他用户的用户的帐户.

我的问题是,我测试所有这些与我FakeUserRepository实施后,我要回去和执行我的实际持久性数据集中IUserRepository(IG SQL),我不得不再次实现代码,所以我的单元测试是不实际检查我实际在我的应用程序上使用的代码.

也许我错过了什么.

一如既往地谢谢!

下面是我的Persistent数据访问存储库,它应该是一个应该被测试的存储库(至少在我看来),但是我不应该测试挂钩到数据库:

public class SQLUserRepository : IUserRepository
{
    private BusinessDomainModel.EntityModel.BusinessHelperAccountDBEntities _repository;

    public SQLUserRepository()
    {
        _repository = new BusinessHelperAccountDBEntities();
    }

    #region IUserRepository Members

    public IQueryable<User> FindAllUsers()
    {
        return _repository.UserSet.AsQueryable();
    }
Run Code Online (Sandbox Code Playgroud)

tva*_*son 12

永远不要测试模拟.被测试的类应该始终是类的真实实例,尽管您可以并且应该模拟其任何依赖项,以便您可以单独测试它.

  • @Geo:通过测试实际的SqlRepository,你已经在Integration-Test中了.对于单元测试,根本不测试SqlRepository是完全有效的. (3认同)
  • 这个答案并没有解决这个问题,只是确认了@Geo的要求.请举个例子 (2认同)

Pur*_*ome 5

我会解释我在做什么,为什么以及我有多少出类型.

首先,我做的正是你在做什么,关于你的资料库.尽管存在一些命名空间差异,但我也是这样做的:

  • MyProject.Repositories.IUserRepository
  • MyProject.Repositories.Fake.UserRepository
  • MyProject.Repositories.SqlServer.UserRepository

使用我的 UserRepository,我也只是创建并填充一个private IEnumerable<User>集合(这是一个List<User>).为什么我这个?我使用这个存储库进行我的初始日常开发(因为它很快 - >没有数据库访问==快!).然后我交换假的respitories为sql存储库(即chage我的依赖注入(oooohhh!)).这就是为什么这个类/命名空间存在,而不是在我的单元测试中使用Mocks来实现'假'的东西.(这种情况会发生,但在不同情况下).

使用我的sql server UserRepository,我使用LinqToSql.关于你的问题,我使用LinqToSql是不可能的...它可能是任何其他数据库包装器.这里重要的是,我正在整合第三方的东西.


好的,所以从这里开始,我需要确定两件事

  1. 虚假的UserRepostiory有效
  2. sql server UserRepository工作正常.

首先,大多数人不会为假货创建单元测试.这是一块假的粪便,为什么要浪费能量呢?没错 - 除了我在我的日常开发中使用那块假粪便(请参阅我上面提到的这一点,上面的内容).所以我很快就开始进行一些基本的单元测试.注意:在我看来,这些是单元测试,即使它们是repository类.为什么?他们没有与第三方/基础设施整合.

接下来(最后我说到了),我做了一个单独的测试课,这是一个综合测试.这是一个单元测试,可以与系统外部的东西集成.它可能是真正的Twitter api.它可能是真正的S3 Amazon api.请注意,我使用了单词real.这是关键所在.我正在与我的外面的真实服务融为一体.因此 - >它很慢.任何时候我需要离开我的计算机获取一些数据,它被称为集成,你自动假设(并期望)它很慢.

所以在这里,我正在与数据库集成.

(Nae说道,请不要因为你在同一台计算机上拥有数据库的厚颜无耻的建议而欺骗这个......你要离开你的应用'世界').

哇.这是一些War-n-Peace小说..时间为一些硬性动作,公鸡slappin代码.让我们来吧!

namespace MyProject.Tests.Repositories.SqlServer
{
    // ReSharper disable InconsistentNaming

    [TestClass]
    public class UserRepositoryTests : TestBase
    {
        [ClassInitialize]
        public static void ClassInitialize(TestContext testContext)
        {
            // Arrange.
            // NOTE: this method is inherited from the TestBase abstract class.
            // Eg. protected IUserRepository = 
            //     new MyProject.Respositories.SqlServer
            //         .UserRespository(connectionString);
            InitializeSqlServerTestData();
        }

        [TestMethod]
        public void GetFirst20UsersSuccess()
        {
            // Act.
            var users = _users.GetUsers()
                .Take(20)
                .ToList();

            // Assert.
            Assert.IsNotNull(users);
            Assert.IsTrue(users.Count() > 0);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

好吧,让我们来看看这只小狗.

首先,这是使用Microsoft单元测试 - 内置到VS2010 Beta2或VS2008的Team Foundation版本(或任何版本的......我只是安装我们的工作已经购买的副本).

其次,无论何时首次初始化类(无论是一个测试还是多个),它都会创建context.就我而言,我的Sql Server UserRepository将使用LinqToSql上下文.(你的将是一个EF背景).这是TDD 的安排部分.

第三,我称之为方法 - >这是TDD 的Act部分.

最后,我检查我是否回到了我的预期 - >这是TDD 的Assert部分.


如何更新数据库?

只需遵循相同的模式,除非您可能希望将您的调用代码包装在事务中并将其回滚.否则你可能得到100行的数据可能是相同的.对此有何不妥?任何identity字段都没有所有漂亮和漂亮的编号序列(因为回滚将"使用"该数字).不明白吗?别担心 这是一个高级提示,我以为我会投入测试你,但这意味着蹲下来这个非常长篇大论的帖子.


所以..呃..是的.我就是做这个的.不知道在这些论坛上,编程之神是否会翻转并抛弃我的方式,但我有点喜欢它,我希望它可以帮助你.

HTH.