Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoFixture.AutoMoq supply a known value for one constructor parameter

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.

like image 456
Nick Soper Avatar asked May 29 '13 16:05

Nick Soper


2 Answers

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's LifeSpanArg:

public class ParameterNameSpecimenBuilder<T> : ISpecimenBuilder {     private readonly string name;     private readonly T value;      public ParameterNameSpecimenBuilder(string name, T value)     {         // we don't want a null name but we might want a null value         if (string.IsNullOrWhiteSpace(name))         {             throw new ArgumentNullException("name");         }          this.name = name;         this.value = value;     }      public object Create(object request, ISpecimenContext context)     {         var pi = request as ParameterInfo;         if (pi == null)         {             return new NoSpecimen(request);         }          if (pi.ParameterType != typeof(T) ||             !string.Equals(                 pi.Name,                  this.name,                  StringComparison.CurrentCultureIgnoreCase))         {             return new NoSpecimen(request);         }          return this.value;     } } 

I've then defined a generic FreezeByName extension method on IFixture which sets the customization:

public static class FreezeByNameExtension {     public static void FreezeByName<T>(this IFixture fixture, string name, T value)     {         fixture.Customizations.Add(new ParameterNameSpecimenBuilder<T>(name, value));     } } 

The following test will now pass:

[TestMethod] public void FreezeByName_Sets_Value1_And_Value2_Independently() {     //// Arrange     IFixture arrangeFixture = new Fixture();      string myValue1 = arrangeFixture.Create<string>();     string myValue2 = arrangeFixture.Create<string>();      IFixture sutFixture = new Fixture();     sutFixture.FreezeByName("value1", myValue1);     sutFixture.FreezeByName("value2", myValue2);      //// Act     TestClass<string> result = sutFixture.Create<TestClass<string>>();      //// Assert     Assert.AreEqual(myValue1, result.Value1);     Assert.AreEqual(myValue2, result.Value2); }  public class TestClass<T> {     public TestClass(T value1, T value2)     {         this.Value1 = value1;         this.Value2 = value2;     }      public T Value1 { get; private set; }      public T Value2 { get; private set; } } 
like image 123
Nick Soper Avatar answered Sep 22 '22 16:09

Nick Soper


You have to replace:

string knownValue = fixture.Freeze<string>("My known value"); 

with:

fixture.Inject("My known value"); 

You can read more about Inject here.


Actually the Freeze extension method does:

var value = fixture.Create<T>(); fixture.Inject(value); return value; 

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".

like image 43
Nikos Baxevanis Avatar answered Sep 22 '22 16:09

Nikos Baxevanis