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

C# 如何使用ADO.NET实体框架测试存储库模式?,c#,asp.net-mvc,design-patterns,tdd,repository-pattern,C#,Asp.net Mvc,Design Patterns,Tdd,Repository Pattern,在使用存储库模式时,我发现很难理解使用TDD技术设计软件的原因,而实际上,您必须在持久性数据集中实现存储库的接口 为了阐明我的观点,我将提交一个示例: 我的域模型上有以下接口: public interface IUserRepository { IQueryable<User> FindAllUsers(); void AddUser(User newUser); User GetUserByID(int userID); void Update(U

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

为了阐明我的观点,我将提交一个示例:

我的域模型上有以下接口:

public interface IUserRepository
{
    IQueryable<User> FindAllUsers();
    void AddUser(User newUser);
    User GetUserByID(int userID);
    void Update(User userToUpdate);
}
公共接口存储库
{
IQueryable FindAllUsers();
void AddUser(User newUser);
用户GetUserByID(int userID);
无效更新(用户userToUpdate);
}
出于测试目的,我有以下接口实现:

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
    }
公共类FakeUserRepository:IUserRepository
{
私有IList_存储库;
公共FakeUserRepository()
{
_repository=新列表();
…//为测试目的创建所有用户
}
公共可查询FindAllUsers()
{
return _repository.AsQueryable();//返回所有用户
}
现在,我创建了几个测试:

  • 可以添加用户吗
  • 可以为用户添加帐户吗
  • 可以为一个用户与另一个用户的帐户添加共享空间
  • 我的问题是,在我用FakeUserRepository实现测试了所有这些之后,我必须返回并在我的实际持久性数据集(如SQL)上实现IUserRepository,并且我必须再次实现代码,因此我的单元测试实际上并不是检查我在应用程序上实际使用的代码

    也许我错过了什么

    一如既往地谢谢你

    下面是我的持久数据访问存储库,它应该是一个正在测试的存储库(至少在我看来),但是我不应该测试连接到数据库的存储库:

    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();
        }
    
    公共类SQLUserRepository:IUserRepository
    {
    private BusinessDomainModel.EntityModel.BusinessHelperAccountDBEntities\u存储库;
    公共SQLUserRepository()
    {
    _repository=new BusinessHelperAccountDBEntities();
    }
    #区域存储库成员
    公共可查询FindAllUsers()
    {
    返回_repository.UserSet.AsQueryable();
    }
    
    您应该测试真实的类,而不是为了测试而制作的假类。使用接口的意义在于它允许您模拟类,因此您可以在与其他合作者的测试中使用模拟版本

    为了测试该类,您应该能够传入一个模拟数据库,并断言当您调用存储库类上的方法时,您期望对该数据库进行的调用实际上发生了

    下面是一个很好的C#模拟和测试介绍:


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

    我将解释我在做什么,为什么以及我从中得到了多少信息

    首先,关于您的存储库,我所做的正是您所做的。尽管存在一些名称空间差异,但这也是我所做的:

    • MyProject.Repositories.IUserRepository
    • MyProject.Repositories.Fake.UserRepository
    • MyProject.Repositories.SqlServer.UserRepository
    使用我的假UserRepository,我还只是创建并填充一个
    私有IEnumerable
    集合(这是一个
    列表
    )。为什么我会有这个?我使用这个存储库进行我的初始日常开发(因为它很快->无db访问==快速!)。然后我将假职责替换为sql存储库这就是为什么这个类/名称空间存在的原因,而不是在我的单元测试中使用mock来“伪造”东西(这种情况会发生,但情况不同)

    对于我的sql server UserRepository,我使用LinqToSql。关于您的问题,我使用LinqToSql是不现实的……它可以是任何其他数据库包装器。这里重要的是,我正在与第三方进行集成


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

  • 假用户报告有效
  • sql server用户存储库工作正常
  • 首先,大多数人不会为一个假的东西创建一个单元测试。这是一个假的废话,那么为什么要浪费精力呢?是的——除了我在日常开发中使用这个假废话(参考上面我的废话)。因此,我很快制定了一些基本的单元测试。注意:在我看来,这些都是单元测试,即使它们是
    存储库
    类。为什么?它们没有与第三方/基础设施集成

    接下来(我终于说到点子上了),我做了一个单独的测试类,这是一个集成测试。这是一个单元测试,将与系统之外的东西集成。它可能是真正的Twitter api。它可能是真正的S3亚马逊api。注意,我使用了real这个词。这是关键。我正在与我之外的真正服务集成。因此->它很慢。任何时候我需要离开我的电脑去获取一些数据,这叫做积分,你会自动地认为(并期望)它会变慢

    在这里,我将与一个数据库集成

    (Nae sayers,请不要用厚颜无耻的建议来搪塞这个问题,说你的数据库在同一台计算机上……你要离开你的应用程序“世界”)

    哇。这是一部战争与和平小说。是时候采取一些强硬的行动了,这是一个愚蠢的密码。 让我们来吧

    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);
            }
        }
    }
    
    好的,让我们看看这只小狗

    首先,这是使用微软单元测试——内置到VS2010 BETA2或使用VS2008的Team Foundation版本(或者不管那个版本是什么……我只安装我们的工作已经购买的副本)。 其次,每当类首次初始化时(无论是一个测试还是多个测试),它都会创建

    上下文
    。在我的例子中,我的Sql Server UserRepository将使用LinqToSql上下文。(您的将是EF上下文)。这是TDD的排列部分

    第三,我调用方法->这是TDD的Act部分