当有多个子实体路径时,如何生成对象?

Bra*_*bby 5 c# autofixture

我正在开发一个具有与此类似的域模型的应用程序,其中a LineItem既可以从a 也可以Order从a 引用Shipment.

图

如果我使用AutoFixture生成Order,我该如何使用相同的一组LineItems两个order.LineItemsorder.Shipments*.ItemShipments*.LineItem

从理论上讲,以下测试应该通过:

var fullyShippedOrder = fixture.CreateAnonymous<Order>();
var shippedLineItems = fullyShippedOrder.Shipments
    .SelectMany(o => o.ItemShipment, (s, i) => i.LineItem)
    .Distinct();
Assert.EqualCollection(fullyShippedOrder.LineItems, shippedLineItems);
Run Code Online (Sandbox Code Playgroud)

...虽然我也希望能够根据测试生成部分发货的订单.

(有一个可靠的论据,即订单上的订单项和货件上的订单项是不同的东西,我不应该使用相同的类来表示它们.但是,我正在使用的数据来自遗留系统并没有太多可以做的事情.)

Enr*_*lio 5

正如您所说,您在测试中遇到的摩擦很可能是域模型中设计问题的一个标志.理想情况下,您应该听取您的测试并从根本上解决问题.但是,鉴于在这种情况下不可行,这是一个解决方法.

您可以使用该方法配置Fixture始终LineItem从固定序列返回对象Register<T>(Func<T> creator).

以下是自定义打包的示例:

public class GenerateLineItemFromFixedSequence : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var items = CreateFixedLineItemSequence(fixture);
        fixture.Register(() => GetRandomElementInSequence(items));
    }

    private static IEnumerable<LineItem> CreateFixedLineItemSequence(IFixture fixture)
    {
        return fixture.CreateAnonymous<LineItem[]>();
    }

    private static LineItem GetRandomElementInSequence(IEnumerable<LineItem> items)
    {
        var randomIndex = new Random().Next(0, items.Count());
        return items.ElementAt(randomIndex);
    }
}
Run Code Online (Sandbox Code Playgroud)

要在测试的上下文中应用此行为,只需将自定义添加到Fixture对象:

fixture.Customize(new GenerateLineItemFromFixedSequence());
Run Code Online (Sandbox Code Playgroud)

同样,您可以创建其他自定义项,以生成LineItem不同状态的固定对象序列,例如您提到的部分出货订单,并在不同的测试中使用它们.

一个观察

值得注意的是,这种自定义可以是通用的,因为算法本身并不与正在创建的对象类型相结合.这将有效地将其转化为战略.

因此,通过引入通用参数来修改自定义:

public class GenerateFromFixedSequence<T> : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var items = CreateFixedSequence(fixture);
        fixture.Register(() => GetRandomElementInSequence(items));
    }

    private static IEnumerable<T> CreateFixedSequence(IFixture fixture)
    {
        return fixture.CreateAnonymous<T[]>();
    }

    private static T GetRandomElementInSequence(IEnumerable<T> items)
    {
        var randomIndex = new Random().Next(0, items.Count());
        return items.ElementAt(randomIndex);
    }
}
Run Code Online (Sandbox Code Playgroud)

允许你将它用于不同的对象:

fixture.Customize(new GenerateFromFixedSequence<LineItem>());
fixture.Customize(new GenerateFromFixedSequence<Order>());
fixture.Customize(new GenerateFromFixedSequence<Shipment>());
Run Code Online (Sandbox Code Playgroud)