I've just started to use AutoFixture.AutoMoq in my unit tests and I'm finding it very helpful for creating objects where I don't care about the specific value. After all, anonymous object creation is what it is all about.
What I'm struggling with is when I care about one or more of the constructor parameters. Take ExampleComponent
below:
public class ExampleComponent
{
public ExampleComponent(IService service, string someValue)
{
}
}
I want to write a test where I supply a specific value for someValue
but leave IService
to be created automatically by AutoFixture.AutoMoq.
I know how to use Freeze
on my IFixture
to keep hold of a known value that will be injected into a component but I can't quite see how to supply a known value of my own.
Here is what I would ideally like to do:
[TestMethod]
public void Create_ExampleComponent_With_Known_SomeValue()
{
// create a fixture that supports automocking
IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());
// supply a known value for someValue (this method doesn't exist)
string knownValue = fixture.Freeze<string>("My known value");
// create an ExampleComponent with my known value injected
// but without bothering about the IService parameter
ExampleComponent component = this.fixture.Create<ExampleComponent>();
// exercise component knowning it has my known value injected
...
}
I know I could do this by calling the constructor directly but this would no longer be anonymous object creation. Is there a way to use AutoFixture.AutoMock like this or do I need to incorporate a DI container into my tests to be able to do what I want?
EDIT:
I probably should have been less absract in my original question so here is my specific scenario.
I have an ICache
interface which has generic TryRead<T>
and Write<T>
methods:
public interface ICache
{
bool TryRead<T>(string key, out T value);
void Write<T>(string key, T value);
// other methods not shown...
}
I'm implementing a CookieCache
where ITypeConverter
handles converting objects to and from strings and lifespan
is used to set the expiry date of a cookie.
public class CookieCache : ICache
{
public CookieCache(ITypeConverter converter, TimeSpan lifespan)
{
// usual storing of parameters
}
public bool TryRead<T>(string key, out T result)
{
// read the cookie value as string and convert it to the target type
}
public void Write<T>(string key, T value)
{
// write the value to a cookie, converted to a string
// set the expiry date of the cookie using the lifespan
}
// other methods not shown...
}
So when writing a test for the expiry date of a cookie, I care about the lifespan but not so much about the converter.
So I'm sure people could work out the generalized implementation of Mark's suggestion but I thought I'd post it for comments.
I've created a generic
ParameterNameSpecimenBuilder
based on Mark'sLifeSpanArg
:I've then defined a generic
FreezeByName
extension method onIFixture
which sets the customization:The following test will now pass:
You could do something like this. Imagine that you want to assign a particular value to the
TimeSpan
argument calledlifespan
.Imperatively, it could be used like this:
This approach can be generalized to some degree, but in the end, we're limited by the lack of a strongly typed way to extract a ParameterInfo from a particular constructor or method argument.
You have to replace:
with:
You can read more about
Inject
here.Actually the
Freeze
extension method does:Which means that the overload you used in the test actually called
Create<T>
with a seed: My known value resulting in "My known value4d41f94f-1fc9-4115-9f29-e50bc2b4ba5e".I fee like @Nick was almost there. When overriding the constructor argument, it needs to be for the given type and have it limited to that type only.
First we create a new ISpecimenBuilder that looks at the "Member.DeclaringType" to keep the correct scope.
Next we create an extension method to allow us to easily wire it up with AutoFixture.
Now we create two similar classes to test with.
Finally we test it with an extension of the original test to see that it will not override similarly named and typed constructor arguments.
This seems to be the most comprehensive solution set here. So I'm going to add mine:
The first thing to create
ISpecimenBuilder
that can handle multiple constructor parametersThen create extension method that simplifies usage of created builder
And usage: