Given the two classes:
class Foo
{
...
}
class Bar
{
public Foo FooBar { get; set; }
}
I have set up the following test:
void Test()
{
var fixture = new Fixture();
fixture.Customize<Foo>(x => x.FromSeed(TestFooFactory));
var fooWithoutSeed = fixture.Create<Foo>();
var fooWithSeed = fixture.Create<Foo>(new Foo());
var bar = fixture.Create<Bar>(); //error occurs here
}
Foo TestFooFactory(Foo seed)
{
//do something with seed...
return new Foo();
}
I can create Foo
objects directly with and without seed values without any problem. But once I try to create a Bar
object that has a Foo
property, I get an ObjectCreationException
:
The decorated ISpecimenBuilder could not create a specimen based on the request: Foo. This can happen if the request represents an interface or abstract class; if this is the case, register an ISpecimenBuilder that can create specimens based on the request. If this happens in a strongly typed Build expression, try supplying a factory using one of the IFactoryComposer methods.
I'd expect TestFooFactory
to get passed a null
seed value during the creation of Bar
, just as when I created Foo
without a seed value. Am I doing something wrong, or could this be a bug?
In my real-world scenario, I want to customize how AutoFixture would use seeded values for certain objects when I pass seeded values in, but I still want AutoFixture to default to normal behavior if no seed is provided.
The way you're customizing the Fixture
to use seed values is correct.
The behavior you're seeing is a consequence of how the FromSeed
customization modifies the AutoFixture pipeline. If you're interested in reading up on the details, I've described them here.
As a workaround, you can use a custom specimen builder for seeded requests like this one:
public class RelaxedSeededFactory<T> : ISpecimenBuilder
{
private readonly Func<T, T> create;
public RelaxedSeededFactory(Func<T, T> factory)
{
this.create = factory;
}
public object Create(object request, ISpecimenContext context)
{
if (request != null && request.Equals(typeof(T)))
{
return this.create(default(T));
}
var seededRequest = request as SeededRequest;
if (seededRequest == null)
{
return new NoSpecimen(request);
}
if (!seededRequest.Request.Equals(typeof(T)))
{
return new NoSpecimen(request);
}
if ((seededRequest.Seed != null)
&& !(seededRequest.Seed is T))
{
return new NoSpecimen(request);
}
var seed = (T)seededRequest.Seed;
return this.create(seed);
}
}
You can then use it to create objects of type Foo
like this:
fixture.Customize<Foo>(c => c.FromFactory(
new RelaxedSeededFactory<Foo>(TestFooFactory)));
This customization will pass default(Foo)
– that is null
– as a seed to the TestFooFactory
factory function when populating properties of type Foo
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With