Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create concrete type for abstract property depending on context

I have the following type hierarchy:

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

I want AutoFixture to create the 'right' type of ResourceId when it tries to create a Plane or Car.

I tried using:

_fixture.Customize<Plane>(composer => 
    composer.With(h => h.Id, _fixture.Create<PlaneId>()));

But of course that creates the same ID for each instance. I want each to have a unique ID.

What is the best approach for this?

like image 626
Jack Ukleja Avatar asked Aug 26 '14 05:08

Jack Ukleja


1 Answers

There's a way to do what you ask for but it requires a lesser-known customization API: the Register method.

The purpose of Register is to allow the user to specify a factory function for a specific type. AutoFixture will then delegate the creation of objects of that type to the function.

One distinctive feature of Register is that it's able to provide anonymous objects to pass as arguments to the factory function whenever they're needed. It does so through a number of overloads. Here are a few examples:

  • Register<T>(Func<T> factory) doesn't provide any arguments to the factory
  • Register<T1, T>(Func<T1, T> factory) provides one argument of type T1 to create objects of type T
  • Register<T1, T2, T>(Func<T1, T2, T> factory) provides two arguments of type T1 and T2 to create objects of type T

Now, we can use this feature to customize the way objects of type Car and Plane are created by assigning anonymous instances of CarId and PlanId to their Id properties:

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

This test passes because the CarId and PlanId objects that are passed to the factory functions are created by AutoFixture, thus being different every time.

like image 182
Enrico Campidoglio Avatar answered Sep 30 '22 11:09

Enrico Campidoglio