Pregunta

I can't seem to find much information on this so I thought I'd bring it up here. One of the issues I often find myself running into is unit testing the creation of a single object while processing a list. For example, I'd have a method signature such as IEnumerable<Output> Process(IEnumerable<Input> inputs). When unit testing a single input I would create a list of one input and simply call First() on the results and ensure it is what I expect it to be. This would lead to something such as:

public class BatchCreator
{
    public IEnumerable<Output> Create(IEnumerable<Input> inputs)
    {
        foreach (var input in inputs)
        {
            Console.WriteLine("Creating Output...");
            yield return new Output();
        }
    }
}

My current thinking is that maybe one class should be responsible for the objects creation while another class be responsible for orchestrating my list of inputs. See example below.

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));
    }
}

With what's been posted above, I can easily test that I'm able to create one single instance of Output given an Input. Note that I do not need to call SingleCreator anywhere else in the code base other than from CompositeCreator. Creating ICreator would also give me the benefit of reusing it for other times I need to do similar tasks, which I currently do 2-3 other times in my current project

Anyone have any experience with this that could shed some light? Am I simply overthinking this? Suggestions are greatly appreciated.

¿Fue útil?

Solución

Generally speaking, there's nothing inherently wrong with your reasoning. More or less that's how the issue can be solved.

However, your CompositeCreator isn't actually composite, since it uses precisely one "creation method".

It's difficult to say anything more, because we don't know your project internals, but if it integrates well into your use cases, then it's fine. What I'd try is stay with ICreator<Tin, Tout> only and make an extension method IEnumerable<Tout> CreateMany(this IEnumerable<Tin> c) to deal with collections. You can test both easily, independently (fake ICreator and check whether collection of inputs is processed). This way you get rid of ICreator<IEnumerable, ...>, which is usually good, because operating on collection as a whole and operating on individual items often don't go well together.

Otros consejos

I'm not entirely sure why you need the IEnumerable input/output option, the composite creator, unless it is more than just a collection, as that's a problem solved by LINQ, which would look something like:

var singleCreator = new SingleCreator();
var outputs = InputEnumerable.Select(singleCreator.Create);

I think this is subjective, and depends on the complexity of the classes you are passing around - if it's not just an IEnumerable then it's worthwhile having some sort of multiple creator, which may or may not need to be a class.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top