Joh*_*ohn 6 unit-testing entity-framework moq repository-pattern
我决定开始在我们的应用程序中编写单元测试.它使用Entity Framework和存储库模式.
现在我想开始测试使用存储库的逻辑类.我在这里提供一个简单的例子.
GenericRepository类中的三个方法:
public class GenericRepository : IRepository
{
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return Context.CreateQuery<TEntity>(entityName);
}
private string GetEntityName<TEntity>() where TEntity : class
{
return typeof(TEntity).Name;
}
public IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
return GetQuery<TEntity>().Where(predicate).AsEnumerable();
}
}
Run Code Online (Sandbox Code Playgroud)
一个简单的逻辑类以递减的顺序从日历表中返回不同的年份(是的,我知道单词日历在我们的代码中拼写错误):
public class GetDistinctYearsFromCalendar
{
private readonly IRepository _repository;
public GetDistinctYearsFromCalendar()
{
_repository = new GenericRepository();
}
internal GetDistinctYearsFromCalendar(IRepository repository)
{
_repository = repository;
}
public int[] Get()
{
return _repository.Find<Calender_Tbl>(c => c.Year.HasValue).Select(c => c.Year.Value).Distinct().OrderBy(c => c).Reverse().ToArray();
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的第一次测试:
[TestFixture]
public class GetDistinctYearsFromCalendarTest
{
[Test]
public void ReturnsDistinctDatesInCorrectOrder()
{
var repositoryMock = new Mock<IRepository>();
repositoryMock.Setup(r => r.Find<Calender_Tbl>(c => c.Year.HasValue)).Returns(new List<Calender_Tbl>
{
new Calender_Tbl
{
Date =
new DateTime(2010, 1, 1),
Year = 2010
},
new Calender_Tbl
{
Date =
new DateTime(2010, 2, 1),
Year = 2010
},
new Calender_Tbl
{
Date =
new DateTime(2011, 1, 1),
Year = 2011
}
}.AsQueryable());
var getDistinct = new GetDistinctYearsFromCalendar(repositoryMock.Object).Get();
Assert.AreEqual(2, getDistinct.Count(), "Returns more years than distinct.");
Assert.AreEqual(2011, getDistinct[0], "Incorrect order, latest years not first.");
Assert.AreEqual(2010, getDistinct[1], "Wrong year.");
}
}
Run Code Online (Sandbox Code Playgroud)
这工作正常.但这实际上并不是我想要做的.由于我必须在模拟对象上设置Find方法,我还需要知道它将如何在我的逻辑类中调用.如果我想做TDD,我不想介意这一点.我想知道的是我的存储库应该提供哪些日历实体.我想设置GetQuery方法.像这样:
repositoryMock.Setup(r => r.GetQuery<Calender_Tbl>()).Returns(new List<Calender_Tbl>
{
new Calender_Tbl
{
Date =
new DateTime(2010, 1, 1),
Year = 2010
},
new Calender_Tbl
{
Date =
new DateTime(2010, 2, 1),
Year = 2010
},
new Calender_Tbl
{
Date =
new DateTime(2011, 1, 1),
Year = 2011
}
}.AsQueryable());
Run Code Online (Sandbox Code Playgroud)
因此,当Find在GenericRepository类中内部调用GetQuery时,它应该获取我在GetQuery中设置的正确Calendar实体.但这当然不起作用.由于我没有设置我的模拟对象的Find方法,所以我没有得到任何实体.
那么该怎么办?当然我可能会使用Moles或其他一些模仿一切的框架,但我不想这样做.在设计课程或测试时我能做些什么来解决这个问题?
如果我必须使用我当前的解决方案,那么这不是世界末日,但如果财产年变成不可空的int会怎样?当然,我将不得不在逻辑类中更改我的实现,但我还必须更改测试.我想尽量避免这种情况.
kzu*_*kzu 12
我可以看到两种方式:
public class MockRepository : IRepository
{
private List<object> entities;
public MockRepository(params object[] entitites)
{
this.entities = entities.ToList();
}
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
return this.entities.OfType<TEntity>().AsQueryable();
}
public IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
return GetQuery<TEntity>().Where(predicate).AsEnumerable();
}
}
Run Code Online (Sandbox Code Playgroud)
这是最简单的,也是我首选的方式.Moq不是一切的锤子;)
或者,如果你真的坚持使用Moq(我很受宠若惊,但在这种情况下非常不必要,因为你可以对返回的实体进行基于状态的测试),你可以这样做:
public class GenericRepository : IRepository
{
public virtual IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return Context.CreateQuery<TEntity>(entityName);
}
private string GetEntityName<TEntity>() where TEntity : class
{
return typeof(TEntity).Name;
}
public IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
return GetQuery<TEntity>().Where(predicate).AsEnumerable();
}
}
Run Code Online (Sandbox Code Playgroud)
然后使用Moq覆盖GetQuery的行为:
var repository = new Mock<GenericRepository> { CallBase = true };
repository.Setup(x => x.GetQuery<Foo>()).Returns(theFoos.AsQueryable());
Run Code Online (Sandbox Code Playgroud)
会发生的是,Find方法将在GenericRepository类上执行,而GenericRepository类又将由Moq覆盖的GetQuery提供固定的实体集.
我明确地设置了CallBase = true,以防万一你碰巧发现了Find虚拟,所以我们确保它总是被调用.如果Find不是虚拟的,则技术上不需要,因为它总是在mock继承/模拟的实际类上调用.
我会选择第一个选项,更容易理解发生了什么,并且可以在单个特定测试的上下文之外重用(只需传递您需要的任何实体,它将适用于所有内容).
| 归档时间: |
|
| 查看次数: |
6692 次 |
| 最近记录: |