Autofixture+Xunit: How to inject XunitLogger<T> from ILogger<T> #1369
Replies: 2 comments
-
@dilandau2001 unfortunately XUnit 2 does not expose a lot of integration points that we could use to capture the output helper instantiated by the test runner. The situation might change in XUnit 3, but until then this is impossible to do via the data attribute. The only way to inject the custom logger, that I can think of at the moment, is to let XUnit inject the fixture in the test constructor. You'll have to give up the data attribute, but you'll be able to see the logs in the Test Explorer window. public class DomainFixture : Fixture
{
public DomainFixture() : base()
{
this.OmitAutoProperties = true;
this.Customize(new AutoMoqCustomization());
}
}
public class UnitTest1 : IClassFixture<DomainFixture>
{
private readonly ITestOutputHelper outputHelper;
private readonly DomainFixture fixture;
public UnitTest1(ITestOutputHelper outputHelper, DomainFixture fixture)
{
this.outputHelper = outputHelper;
this.fixture = fixture;
var provider = new XUnitLoggerProvider(outputHelper);
this.fixture.Inject(provider.CreateLogger<MyTestClass>());
}
[Fact]
public void WhenThen()
{
// Arrange
var sut = fixture.Create<MyTestClass>();
// Act
var res = sut.Example();
// Assert
Assert.True(res);
}
} |
Beta Was this translation helpful? Give feedback.
-
https://github.com/apazureck/AutoFixture.Community.LoggerFactoryCustomizations/blob/master/README.md this should simplfy the process a bit |
Beta Was this translation helpful? Give feedback.
-
Problem Description
In this project I am using Autofixture to automatically create classes under test with the minimum amount of code possible.
For that I have followed the quickstart to create the AutoDomainDataAttribute.
https://autofixture.github.io/docs/quick-start/#
You end up with the following code
With that attribute you can create tests like the following:
Where MyTestClass is the following:
Note that this class needs a
ILogger<T>
to be constructed, and that is done by autofixture creating aMock<ILogger<T>>
automatically.Objective/Goal
My goal is somehow inject the Xunit _testOutputHelper so traces from the tested class appears in the test log.
So for the given code, test output would say
[DBG] MyTestClass => Example => My Test.
Constraints
ILogger
comes from Microsoft Logging extensions and most of its members are extension methods. Which means that they cannot be mocked in a setup. In order to setup something you would need it to be strict behavior and setup over the base functions. See LinkMock<T>
cannot change its behaviour once created. LinkWhat I tried so far
0 - Make a static helper with inputs ILogger and _testOutputHelper to setup all possible uses of the logger inside the tested class.
I can get to the ILogger instance by using [Frozen]. This approach would have work fine if ILogger from Microsoft not having extension methods as it has. You still will miss traces from inside the constructor and you cannot setup over extension methods, and you cannot change behaviour once the mock is created. (See constraints)
1- Customize the fixture so the mocks created are strict. See Link. It fails on constructors that make use of the mocked type inside the constructor, because you had no time to setup the mock. And I don't know why it only applies strict to the first mocked parameter.
2- Try to customize the AutoDomainData to identify
ILogger<T>
to create a XunitLogger. Fixture doesn't support generic types.3 - Create a Xunit Logger provider See Link. Yes, I have a provider, but how do I tell Autofixture to use my provider? if autofixture cannot recognize the generic type. Is there something like a container I could register things to so autofixture try to resolve constructors using it?
4 - Use Moq.Automocker. See Link. I lack the knowledge to add automocker to the Autofixture fixture pipeline. I feel this is similar to point 3. Not sure If this would work although I think it could. (except for the TestoutputHelper constraint which is not singleton)
My current working way
Although ugly my current approach is to make loggers public so I can set them inside the test. I am still missing the calls inside the constructor and I am overriding the injected instance.
Beta Was this translation helpful? Give feedback.
All reactions