我有一个解决方案,其中我有一个数据项目,其中包含从现有数据库生成的EF6 .edmx文件.我将实体拆分为一个单独的实体项目,并有一个引用它们的存储库项目.
我添加了一个带有一些常用方法的BaseRepository,并希望对它进行单元测试.班级的顶端看起来像这样……
public class BaseRepository<T> : BaseRepositoryInterface<T> where T : class {
private readonly MyEntities _ctx;
private readonly DbSet<T> _dbSet;
public BaseRepository(MyEntities ctx) {
_ctx = ctx;
_dbSet = _ctx.Set<T>();
}
public IEnumerable<T> GetAll() {
return _dbSet;
}
//...
}
按照我在https://*.com/a/21074664/706346找到的代码,我尝试了以下内容……
[TestMethod]
public void BaseRepository_GetAll() {
IDbSet<Patient> mockDbSet = Substitute.For<IDbSet<Patient>>();
mockDbSet.Provider.Returns(GetPatients().Provider);
mockDbSet.Expression.Returns(GetPatients().Expression);
mockDbSet.ElementType.Returns(GetPatients().ElementType);
mockDbSet.GetEnumerator().Returns(GetPatients().GetEnumerator());
MyEntities mockContext = Substitute.For<MyEntities>();
mockContext.Patients.Returns(mockDbSet);
BaseRepositoryInterface<Patient> patientsRepository
= new BaseRepository<Patient>(mockContext);
List<Patient> patients = patientsRepository.GetAll().ToList();
Assert.AreEqual(GetPatients().Count(), patients.Count);
}
private IQueryable<Patient> GetPatients() {
return new List<Patient> {
new Patient {
ID = 1,
FirstName = "Fred",
Surname = "Ferret"
}
}
.AsQueryable();
}
请注意,我更改了上下文TT文件以使用IDbSet,正如Stuart Clement在2015年12月4日22:41的评论中所建议的那样
但是,当我运行此测试时,它会给出一个空引用异常,因为基本存储库构造函数中设置_dbSet的行会将其保留为null …
_dbSet = _ctx.Set<T>();
当我设置我的模拟上下文时,我猜我需要添加另一行,但我不确定是什么.我认为上面的代码应该足以填充DbSet.
有谁能解释我错过了什么或做错了什么?
解决方法:
好吧,我试图按照我在问题中展示的方式让自己疯狂,我遇到了Effort,这是专为此任务而设计的,并且遵循了this tutorial,这让我开始了.我的代码有一些问题,我将在下面解释.
简而言之,我所做的是……
*)在测试项目中安装Effort.EF6.我一开始犯了一个错误并安装了Effort(没有EF6位),并且遇到了各种各样的问题.如果您使用的是EF6(或我认为的EF5),请确保安装此版本.
*)修改了MyModel.Context.tt文件以包含一个带有DbConnection的额外构造函数… public MyEntities(DbConnection connection):base(connection,true){}
*)将连接字符串添加到测试项目的App.Config文件中.我从数据项目中逐字复制了这个.
*)为测试类添加了初始化方法以设置上下文…
private MyEntities _ctx;
private BaseRepository<Patient> _patientsRepository;
private List<Patient> _patients;
[TestInitialize]
public void Initialize() {
string connStr = ConfigurationManager.ConnectionStrings["MyEntities"].ConnectionString;
DbConnection connection = EntityConnectionFactory.CreateTransient(connStr);
_ctx = new MyEntities(connection);
_patientsRepository = new BaseRepository<Patient>(_ctx);
_patients = GetPatients();
}
重要 – 在链接的文章中,他使用DbConnectionFactory.CreateTransient(),当我尝试运行测试时,它给出了异常.似乎这是针对Code First的,因为我正在使用Model First,所以我必须将其更改为使用EntityConnectionFactory.CreateTransient().
*)实际测试相当简单.我添加了一些辅助方法来尝试整理它,并使其更具可重用性.在我完成之前,我可能会再进行几轮重构,但这样做有效,并且现在足够干净……
[TestMethod]
public void BaseRepository_Update() {
AddAllPatients();
Assert.AreEqual(_patients.Count, _patientsRepository.GetAll().Count());
}
#region Helper methods
private List<Patient> GetPatients() {
return Enumerable.Range(1, 10).Select(CreatePatient).ToList();
}
private static Patient CreatePatient(int id) {
return new Patient {
ID = id,
FirstName = "FirstName_" + id,
Surname = "Surname_" + id,
Address1 = "Address1_" + id,
City = "City_" + id,
Postcode = "PC_" + id,
Telephone = "Telephone_" + id
};
}
private void AddAllPatients() {
_patients.ForEach(p => _patientsRepository.Update(p));
}
#endregion
这里需要思想转变的一点就是使用Effort,与其他模拟不同,你不会告诉模拟框架为特定参数返回什么.相反,你必须把Effort想象成一个真正的数据库,尽管它是一个临时存储器.因此,我在初始化中设置了一个模拟患者列表,将它们添加到数据库中,然后才进行实际测试.
希望这有助于某人.事实证明,这比我最初尝试的方式要容易得多.