C# 如何使用ADO.NET实体框架测试存储库模式?
在使用存储库模式时,我发现很难理解使用TDD技术设计软件的原因,而实际上,您必须在持久性数据集中实现存储库的接口 为了阐明我的观点,我将提交一个示例: 我的域模型上有以下接口: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
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();//返回所有用户
}
现在,我创建了几个测试:
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
私有IEnumerable
集合(这是一个列表
)。为什么我会有这个?我使用这个存储库进行我的初始日常开发(因为它很快->无db访问==快速!)。然后我将假职责替换为sql存储库这就是为什么这个类/名称空间存在的原因,而不是在我的单元测试中使用mock来“伪造”东西(这种情况会发生,但情况不同)
对于我的sql server UserRepository,我使用LinqToSql。关于您的问题,我使用LinqToSql是不现实的……它可以是任何其他数据库包装器。这里重要的是,我正在与第三方进行集成
好的,从这里开始,我需要确定两件事
存储库
类。为什么?它们没有与第三方/基础设施集成
接下来(我终于说到点子上了),我做了一个单独的测试类,这是一个集成测试。这是一个单元测试,将与系统之外的东西集成。它可能是真正的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部分