单个对象创建时的AutoFixture自定义

时间:2015-11-25 09:54:20

标签: c# autofixture

所以,我有一个具有大量IEnumerable属性的对象。 在单元测试中,我想做这样的事情:

var subsequentAgreement = _fixture.Build<Foo>()
                                          .With(dto => dto.Bars,
                                              _fixture.CreateMany<Bar>())
                                          .Create();

对于其他IEnumerable<T>属性,我想要Enumerable.Empty<T>()

我有ISpecimenBuilder

public class EmptyEnumerableBuilder : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        object returnObject = new NoSpecimen(request);
        var type = request as Type;
        if (type != null && type.IsGenericType)
        {
            var typeArguments = type.GetGenericArguments();
            if(!typeArguments.Any() || typeof(IEnumerable<>) == type.GetGenericTypeDefinition())
                returnObject = Array.CreateInstance(typeArguments.Single(), 0);
        }

        return returnObject;
    }
}

我这样添加:_fixture.Customizations.Add(new EmptyEnumerableBuilder()); 这很好用,除了我现在创建的所有其他对象都有空的枚举。

我正在寻找一种方法将EmptyEnumerableBuilder应用于单个_fixture.Build<>()并保留其余的默认值,但我似乎无法找到方法。

我尝试过使用类似限制:

_fixture.Customize<SubsequentAgreementLimitationsDto>(composer => new EmptyEnumerableBuilder());

但奇怪的是,由fixture创建的所有其他对象仍然具有空的枚举

1 个答案:

答案 0 :(得分:1)

如果您需要以约定方式驱动的内容,则可以使用Albedo清空所有可写IEnumerable<>属性。你可以从这样的事情开始:

public class EmtpyEnumerables : ReflectionVisitor<object>
{
    private object value;

    public EmtpyEnumerables(object value)
    {
        this.value = value;
    }

    public override object Value
    {
        get { return value; }
    }

    public override IReflectionVisitor<object> Visit(PropertyInfoElement propertyInfoElement)
    {
        var pi = propertyInfoElement.PropertyInfo;
        if (pi.PropertyType.IsConstructedGenericType && 
            pi.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>) &&
            pi.CanWrite)
        {
            var elementType = pi.PropertyType.GetGenericArguments().Single();
            pi.SetValue(value, Array.CreateInstance(elementType, 0));
            return this;
        }

        return base.Visit(propertyInfoElement);
    }
}

假设Foo看起来像这样:

public class Foo
{
    public IEnumerable<Bar> Bars { get; set; }

    public IEnumerable<Baz> Bazs { get; set; }

    public IEnumerable<Qux> Quxs { get; set; }

    public string Corge { get; set; }

    public int Grault { get; set; }
}

然后通过以下测试:

[Fact]
public void FillBarsZeroOutAllOtherSequences()
{
    var fixture = new Fixture();

    var actual = fixture.Create<Foo>();
    new TypeElement(actual.GetType()).Accept(new EmtpyEnumerables(actual));
    actual.Bars = fixture.CreateMany<Bar>();

    Assert.NotEmpty(actual.Bars);
    Assert.Empty(actual.Bazs);
    Assert.Empty(actual.Quxs);
    Assert.NotEqual(default(string), actual.Corge);
    Assert.NotEqual(default(int), actual.Grault);
}

如果你认为写出new TypeElement(actual.GetType()).Accept(new EmtpyEnumerables(actual));太麻烦了,我相信你可以想出用辅助方法隐藏它。