AutoData theories with AutoFixture using manual fakes

To check this system:

public class MySut { private readonly IHardToMockDependency _hardToMockDependency; public MySut(IHardToMockDependency hardToMockDependency, IOtherDependency otherDependency) { _hardToMockDependency = hardToMockDependency; } public string GetResult() { return _hardToMockDependency.GetResult(); } } public interface IOtherDependency { } public interface IHardToMockDependency { string GetResult(); } 

And this unit test:

 internal class FakeHardToMockDependency : IHardToMockDependency { private readonly string _result; public FakeHardToMockDependency(string result) { _result = result; } public string GetResult() { return _result; } } public class MyTests { [Fact] public void GetResultReturnsExpected() { string expectedResult = "what I want"; var otherDependencyDummy = new Mock<IOtherDependency>(); var sut = new MySut(new FakeHardToMockDependency(expectedResult), otherDependencyDummy.Object); var actualResult = sut.GetResult(); Assert.Equal(expectedResult, actualResult); } } 

How do I convert it to using AutoFixture.Xunit and AutoFixture.AutoMoq (when using manual fake)?

In real tests, manual fake will have a more complex interface and behavior. Note that I want to pass an anonymous variable (expectedResult string) to the manual fake constructor.

+6
source share
3 answers

There are already some good answers here, but I would like to suggest a simpler alternative that involves slightly weakening the FakeHardToMockDependency class invariants. Make it public and provide a way to assign a result that is separate from the constructor:

 public class FakeHardToMockDependency : IHardToMockDependency { private string _result; public FakeHardToMockDependency(string result) { _result = result; } internal string Result { get { return _result; } set { _result = value; } } public string GetResult() { return _result; } } 

Note that I added an internal property and removed the readonly keyword from this field.

This allows you to reorganize the original test for this:

 [Theory, AutoMoqData] public void GetResultReturnsExpected_AutoDataVersion( [Frozen(As = typeof(IHardToMockDependency))]FakeHardToMockDependency fake, MySut sut) { var expected = "what I want"; fake.Result = expected; var actual = sut.GetResult(); Assert.Equal(expected, actual); } 

For completeness, here is the AutoMoqDataAttribute code:

 public class AutoMoqDataAttribute : AutoDataAttribute { public AutoMoqDataAttribute() : base(new Fixture().Customize(new AutoMoqCustomization())) { } } 
+8
source

Depending on what parameters you need to pass under the fake manually, you can use a parameterized attribute similar to the built-in AutoFixture InlineAutoDataAttribute .

Given these

 public interface IHardToMockDependency { string Value { get; } } public class FakeHardToMockDependency : IHardToMockDependency { private readonly string _value; public FakeHardToMockDependency(string value) { _value = value; } #region IHardToMockDependency Members public string Value { get { return this._value; } } #endregion IHardToMockDependency Members } 

you create an ICustomization implementation that tells the fixture how to create an implementation of the IHardToFakeDependency interface:

 public class FakeHardToMockDependencyCustomization : ICustomization { private readonly string _value; public FakeHardToMockDependencyCustomization(string value) { _value = value; } #region ICustomization Members public void Customize(IFixture fixture) { fixture.Register<IHardToMockDependency>(() => new FakeHardToMockDependency(this._value)); } #endregion ICustomization Members } 

Note that this should know the string you want to pass, of course.

Then you collapse this with the other settings that you want to use in CompositeCustomization :

 public class ManualFakeTestConventions : CompositeCustomization { public ManualFakeTestConventions(string value) : base(new FakeHardToMockDependencyCustomization(value), new AutoMoqCustomization()) { } } 

Make sure you always set the settings in order from the most specific to the most common, as Mark Semann explained here .

Now you create an AutoDataAttribute implementation that uses this setting:

 public class ManualFakeAutoDataAttribute : AutoDataAttribute { public ManualFakeAutoDataAttribute(string value) : base(new Fixture().Customize(new ManualFakeTestConventions(value))) { } } 

Now it can be used in the same way as InlineAutoDataAttribute :

 public class ManualFakeTests { [Theory, ManualFakeAutoData("iksdee")] public void ManualFake(IHardToMockDependency fake) { Assert.IsType<FakeHardToMockDependency>(fake); Assert.Equal("iksdee", fake.Value); } } 

You can also immediately enter it into an automatically created SUT instance by applying the [Frozen] attribute to the Theory parameter:

  [Theory, ManualFakeAutoData("iksdee")] public void SutWithManualFake([Frozen] IHardToMockDependency fake, MySut sut) { } 

This will create an instance of MySut and an instance of IHardToMockDependency , necessary for the constructor for which you provided an AutoFixture rule in FakeHardToMockDependencyCustomization , and also provided this instance as a fake variable.

Note that not FakeHardToMockDependency freeze will still give you the correct instance of FakeHardToMockDependency , and also add it to sut, but they will be different, since we registered the factory delegate in the setting. Freezing an instance will cause the instrument to always return the same instance for subsequent requests for an interface.

There are a few caveats to this:

  • You do not have a link to the string that you pass as a parameter, so you need to have it as a string literal twice. You can get around this with string constants inside the test class, for example.
  • The number of types that can be used as attribute parameters in .NET is limited. So far you only need basic types, you should be fine, but calling constructors, etc. in the parameter list is not possible.
  • You should use this attribute only when you need an instance if IHardToFakeDependency ; otherwise, you will always have to pass a string parameter. If you have a set of standard settings that you need to use, create another attribute containing only those.
  • If you need the capabilities of InlineAutoDataAttribute at the same time, you also need to create another attribute that combines the functions of both.

You can also look at xUnit.net PropertyDataAttribute , depending on the specific circumstances, but I almost never find myself in it.

In general, in my opinion, understanding how to work with settings and attributes of auto data, as well as when and how to create your own, is the key to using AutoFixture effectively and really makes you save.

If you often write code in a specific domain that you need to test, it may well make sense to create a library containing settings, attributes and stub objects that will always be ready for use after you place it next to xUnit. net, AutoFixture and Moq. I know that I am very glad that I created my own.

Oh, and also: the presence of dependencies that are difficult to fake may indicate a design problem. Why is it so hard to taunt?

+4
source

This may not be the most idiomatic Autofixture setup, but it definitely works:

 [Fact] public void GetResultReturnsExpected() { var fixture = new Fixture() .Customize(new AutoMoqCustomization()); var expectedResult = fixture.Create<string>(); fixture.Register<IHardToMockDependency>( () => new FakeHardToMockDependency(expectedResult)); var sut = fixture.Create<MySut>(); var actualResult = sut.GetResult(); Assert.Equal(expectedResult, actualResult); } 

If you want to use AutoData also, you can create your own AutoMoqData based on this wonderful article , where you can hide some or all of the attenuation of devices.

Sort of:

 public class MySutAutoDataAttribute : AutoDataAttribute { public MySutAutoData() : base(new Fixture() .Customize(new AutoMoqCustomization())) { Fixture.Freeze<string>(); Fixture.Register<IHardToMockDependency>( () => new FakeHardToMockDependency(Fixture.Create<string>())); } } 

And you can use it like:

 [Theory, MySutAutoData] public void GetResultReturnsExpected(MySut sut, string expectedResult) { var actualResult = sut.GetResult(); Assert.Equal(expectedResult, actualResult); } 

But you should notice that in MySutAutoDataAttribute there is a place for big improvement: it is not very general, but Fixture.Freeze<string>(); can cause problems if you use multiple lines in your tests.

+3
source

All Articles