我有以下代码:
public class Foo
{
public string Name { get; set; }
}
//query object pattern
public class FooQuery
{
private string _startsWith;
public FooQuery NameStartsWith(string startsWith)
{
_startsWith = startsWith;
return this;
}
public List<Foo> Execute(IQueryable<Foo> someContext)
{
if (!string.IsNullOrWhiteSpace(_startsWith))
someContext = someContext.Where(f => f.Name.StartsWith(_startsWith));
return someContext.ToList();
}
}
public interface IFooService
{
List<Foo> FindByNameStartsWith(string startsWith);
}
public class FooService : IFooService
{
private readonly IFooRepository _fooRepository;
public FooService(IFooRepository fooRepository)
{
_fooRepository = fooRepository;
}
public List<Foo> FindByNameStartsWith(string startsWith)
{
var query = new FooQuery().NameStartsWith(startsWith);
return _fooRepository.Find(query);
}
}
public interface IFooRepository
{
List<Foo> Find(FooQuery query);
}
public class FooRepository : IFooRepository
{
public List<Foo> Find(FooQuery query)
{
var someContext = new List<Foo>().AsQueryable(); //would be EF/Mongo, etc
return query.Execute(someContext);
}
}
基本上,我有一个服务“FooService”,它更新一个查询对象“FooQuery”并根据传递给它的方法参数设置它的状态。然后,该服务将查询传递给存储库“FooRepository”,在该存储库中进行数据访问。FooQuery 故意不通过属性公开其状态。相反,它公开了用于更好控制的方法。我需要对 FooService 已正确创建查询对象进行单元测试。
这是一个挑战,因为 FooQuery 的状态对于单元测试是不可见的。我看到了几个选项,但似乎都闻起来:
将 FooQuery 的状态公开为只读属性,并在单元测试中检查这些属性在传递到存储库时是否有效。(检查查询的状态在技术上可以通过带有回调的模拟框架。)我不喜欢这样,因为我们现在必须打开状态并修改代码只是为了测试目的。
保持代码不变,并测试服务方法生成的结果与查询对象的结果是否相同。我不喜欢这样,因为它使单元测试变得更大、更无定论且更冗余(在检查结果时,我必须对查询对象本身进行非常相似的测试)
将 FooQuery 包装在一个接口中并创建一个要注入 foo 服务的工厂。然后我可以测试在模拟查询上调用的正确方法。但是,这仍然给我留下了对工厂本身的挑战性测试。
任何有关测试/重构此代码以使其更易于测试的建议将不胜感激。