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?