根据上下文为抽象属性创建具体类型

Jac*_*eja 5 c# unit-testing autofixture

我有以下类型层次结构:

public abstract class ResourceId
{
}

public class CarId : ResourceId
{
}

public class PlaneId: ResourceId
{
}

public interface IResource
{
  ResourceId Id { get; set; }
}

public class Plane : IResource
{
  public ResourceId Id { get; set; }
}

public class Car : IResource
{
  public ResourceId Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我希望AutoFixture在尝试创建Plane或Car时创建"正确"类型的ResourceId.

我试过用:

_fixture.Customize<Plane>(composer => 
    composer.With(h => h.Id, _fixture.Create<PlaneId>()));
Run Code Online (Sandbox Code Playgroud)

但是,当然会为每个实例创建相同的ID.我希望每个人都有一个唯一的ID.

对此最好的方法是什么?

Enr*_*lio 5

有一种方法可以满足您的要求,但它需要一个鲜为人知的自定义 API:Register方法。

的目的Register是允许用户为特定类型指定工厂函数。AutoFixture 然后将创建该类型的对象委托给函数。

的一个显着特点Register是它能够提供匿名对象,以便在需要时将它们作为参数传递给工厂函数。它通过许多重载来实现。下面是几个例子

  • Register<T>(Func<T> factory) 不向工厂提供任何参数
  • Register<T1, T>(Func<T1, T> factory)提供一个类型参数T1来创建类型对象T
  • Register<T1, T2, T>(Func<T1, T2, T> factory)提供两个类型的参数T1T2创建类型的对象T

现在,我们可以利用这个特性来定制类型的对象的方式Car,并Plane通过指定的匿名实例被创建CarIdPlanId它们的Id属性:

[Fact]
public void Test()
{
    var fixture = new Fixture();
    fixture.Register<CarId, Car>(id =>
    {
        var resource = new Car { Id = id };
        return resource;
    });
    fixture.Register<PlaneId, Plane>(id =>
    {
        var resource = new Plane { Id = id };
        return resource;
    });

    Assert.NotSame(fixture.Create<Car>().Id, fixture.Create<Car>().Id);
    Assert.NotSame(fixture.Create<Plane>().Id, fixture.Create<Plane>().Id);
}
Run Code Online (Sandbox Code Playgroud)

这个测试通过是因为传递给工厂函数的CarIdPlanId对象是由 AutoFixture 创建的,因此每次都不同。


Mar*_*ann 5

这是一个简单的方法来做你想要的:

[Fact]
public void HowToUseFixtureToCreatePlanesWithNewIds()
{
    var fixture = new Fixture();
    fixture.Customize<Plane>(c => c
        .Without(p => p.Id)
        .Do(p => p.Id = fixture.Create<PlaneId>()));

    var planes = fixture.CreateMany<Plane>();

    Assert.True(planes.Select(p => p.Id).Distinct().Count() > 1);
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我是你,我会认真考虑简化那个类层次结构......


Nik*_*nis 1

您必须为每个类自定义创建算法,在本例中Car是 和Plane,如以下通过测试所示:

[Fact]
public void Test()
{
    var fixture = new Fixture();
    fixture.Customize<Plane>(c => c
        .With(x => x.Id, fixture.Create<PlaneId>()));
    fixture.Customize<Car>(c => c
        .With(x => x.Id, fixture.Create<CarId>()));

    var plane = fixture.Create<Plane>();
    var car = fixture.Create<Car>();

    Assert.IsType<PlaneId>(plane.Id);
    Assert.IsType<CarId>(car.Id);
}
Run Code Online (Sandbox Code Playgroud)