通过分解批处理任务来提高可测试性
我似乎无法找到关于此的很多信息,所以我想我会把它放在这里。我经常发现自己遇到的其中一个问题是在处理列表时单元测试单个对象的创建。例如,我会有一个方法签名,如IEnumerable<Output> Process(IEnumerable<Input> inputs)
。当单元测试单个输入时,我会创建一个输入列表,并简单地调用First()
结果,并确保它是我期望的结果。这将导致一些诸如:通过分解批处理任务来提高可测试性
public class BatchCreator
{
public IEnumerable<Output> Create(IEnumerable<Input> inputs)
{
foreach (var input in inputs)
{
Console.WriteLine("Creating Output...");
yield return new Output();
}
}
}
我现在的想法是,也许一个类应该负责的对象创建,同时另一个类是负责编排我的输入列表。见下面的例子。
public interface ICreator<in TInput, out TReturn>
{
TReturn Create(TInput input);
}
public class SingleCreator : ICreator<Input, Output>
{
public Output Create(Input input)
{
Console.WriteLine("Creating Output...");
return new Output();
}
}
public class CompositeCreator : ICreator<IEnumerable<Input>, IEnumerable<Output>>
{
private readonly ICreator<Input, Output> _singleCreator;
public CompositeCreator(ICreator<Input, Output> singleCreator)
{
_singleCreator = singleCreator;
}
public IEnumerable<Output> Create(IEnumerable<Input> inputs)
{
return inputs.Select(input => _singleCreator.Create(input));
}
}
凭借什么被张贴以上,我可以很容易地测试,我能够创造给予Input
的Output
一个单一实例。请注意,除CompositeCreator
以外,我不需要在代码库中的任何其他地方拨打SingleCreator
。创建ICreator
也会给我重用它的好处,我需要做其他时间做类似的任务,目前我在当前项目中做了2-3次其他时间。
任何人都有这方面的经验可以说明一些情况吗?我只是在推翻这个吗?建议非常感谢。
一般来说,你的推理没有任何内在的错误。或多或少,这就是问题如何解决的问题。
但是,您的CompositeCreator
实际上并不是合成的,因为它只使用一种“创建方法”。
很难说更多,因为我们不知道你的项目内部,但如果它很好地集成到你的用例中,那就没问题。我想尝试的仅仅是ICreator<Tin, Tout>
,并使用扩展方法IEnumerable<Tout> CreateMany(this IEnumerable<Tin> c)
来处理集合。您可以轻松,独立地进行测试(假冒ICreator
并检查是否处理输入集合)。通过这种方式,你可以摆脱ICreator<IEnumerable, ...>
,这通常很好,因为在整个集合上操作并且在单个项目上操作通常不会很好地结合在一起。
我不完全知道为什么你需要的IEnumerable的输入/输出选件,复合材料的创造者,除非它不仅仅是一个收集更多,因为这是由LINQ解决的问题,这将是这个样子:
var singleCreator = new SingleCreator();
var outputs = InputEnumerable.Select(singleCreator.Create);
我认为这是主观的,并取决于您传递的类的复杂性 - 如果它不仅仅是一个IEnumerable,那么值得拥有某种多重创建者,这可能需要也可能不需要成为一个类。
我不得不承认我不明白你想解决的问题。 – 2013-03-19 13:28:00
这对我来说感觉很主观... – penguat 2013-03-19 14:01:34