Hi,
I am trying to use a Moq of a concrete dependency.
[Theory, AutoMoqData]
public async Task Can_register_a_single_service_successfully(
[Frozen] Mock<IdentityServerOptions> identityServerOptions,
ServiceDiscoveryUsingIdentityServer sut
)
{...}
This throws an error where AutoFixture is trying to create a real instance of IdentityServerOptions to inject into my sut instead of using the frozen mock.
If I remove the sut and manually create passing in the identityServerOptions.Object then it all works, so my working theory is that autofixture/automoq is not picking up that the frozen moq can satisfy the dependency and so tries to create a new instance.
I also tried Frozen(Matching.DirectBaseType) as the proxy generated by moq inherits from the requested value, but still no luck.
Is there any way around this as I can't change IdentityServerOptions as it's 3rd party and have more variables to inject to my sut, which makes the workaround a bit messy when I only need to access the one from my example.
Thanks
Euan
Just freeze IdentityServerOptions and use Mock.Get.
Check this page of the booklet I wrote for my developers: https://docs.educationsmediagroup.com/unit-testing-csharp/autofixture/combining-autofixture-with-nunit-and-moq
Unfortunately that doesn't work as IdentityServerOptions is not an abstract class nor an interface and so the AutoMoqCustomization doesn't generate a Moq for it.
For completeness the attribute is:
public AutoMoqDataAttribute()
: base(() => new Fixture().Customize(new AutoMoqCustomization()))
{ }
Note that for interfaces and abstract class it doesn't appear to matter if you freeze the mock wrapper or the interface and then either get the mock or use .Object in your test case.
I believe it is entirely correct that a concrete class in the parameters should give you a real copy, otherwise your sut wouldn't work, but if you specify a dependency with the Mock wrapper to force a mock then Mock<IInterface> works as you would expect, but Mock<Concrete> doesn't.
Then I don't understand your issue. Why do you need to use Moq at all?
IdentityServerOptions is not my class, and can't be instantiated easily from a unit test so I need to Mock it to pass into my sut. The mocking all works great, but it appears AutoFixture/AutoMoq doesn't pick up on the fact that a MockIdentityServerOptions dependency.
Current workaround:
[Theory, AutoMoqData]
public async Task Can_register_a_single_service_successfully(
[Frozen] ILogger<ServiceDiscoveryUsingIdentityServer> logger,
[Frozen] IAuditor<ServiceDiscoveryUsingIdentityServer> auditor,
[Frozen] Mock<IdentityServerOptions> identityServerOptions,
... the other dependencies.
)
{
//Arrange
var sut= new ServiceDiscoveryUsingIdentityServer(logger, auditor, identityServerOptions, identityService, authorizationService, grpcService);
}
This makes it a little messy to have to list all the dependencies in every test when I'm only usually needing the identityServerOptions mock for most of the test cases.
@Euan-McVie sorry for a late reply.
I hope to shed some light into the issue you're experiencing and possibly a workaround that might help you.
While I was not around when AutoMoq was developed, I believe this behavior was intended.
As you probably know AutoMoq can inject mocks two ways either as a mock and as the mocked type.
The current implementation mocks by default any interface or abstract type, given no other customizations have already intercepted the request for the same type. Which is why the auto mocking occurs for requests that have passed through the entire resolution pipeline, right before throwing an exception because the request couldn't be satisfied.
If we were to move the auto-mocking customizations closer to the beginning of the pipeline, then most types would be resolved as mocks. This is not desirable because then the types dependencies would be null or the mock would throw on creation because some guard clauses prevented the creation of the object in an invalid state.
Now it might seem logical that the [Frozen] mocks would be properly resolved, but due to the internal design we cannot tell which types are frozen/injected because the conditions under which the same instance would always be returned are too complex.
A context request for a mock of a mocked type would almost always yield a value, since almost any type can be mocked and we wouldn't know whether the request was satisfied by a frozen value.
As a workaround for your issue I suggest customizing the concrete type to be created from a mock.
public class CustomCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<IdentityServerOptions>(
x => x.FromFactory<IFixture>(
f => f.Create<Mock<IdentityServerOptions>>().Object));
}
}
Since you would have already frozen the mock in your test parameter when you'd requested it in your SUT you would receive the same instance.
[Theory, AutoMoqData]
public async Task Can_register_a_single_service_successfully(
[Frozen] Mock<IdentityServerOptions> identityServerOptions,
ServiceDiscoveryUsingIdentityServer sut)
I will leave this issue open for a little while, in case you have more questions on this topic.
Thanks for the response.
Would it work if I created a custom MockAttribute : CustomizeAttribute so that I could do something like:
[Theory, AutoMoqData]
public async Task Can_register_a_single_service_successfully(
[Frozen, Mock(typeof(IdentityServerOptions))] IdentityServerOptions identityServerOptions,
[Frozen] ILogger<ServiceDiscoveryUsingIdentityServer> logger,
[Frozen] IAuditor<ServiceDiscoveryUsingIdentityServer> auditor,
ServiceDiscoveryUsingIdentityServer sut)
This would be preferred as then the standard AutoMoqData attribute works to generate the other parameters so I can control the specific mocks of concrete objects per test rather than having lots of AutoMoqWithMockXYZ style attributes for other scenarios.
Would this possibly work (not had a chance to test yet myself), or would it not combine properly with the Frozen and underlying AutoMoqData?
public AutoMoqDataAttribute()
: base(() => new Fixture().Customize(new AutoMoqCustomization()))
{ }
Thanks
@Euan-McVie I suppose you could indeed do that. Here's a naive implementation of a customization that could achieve parameter specific mocking
public class MockCustomization : ICustomization
{
public MockCustomization(ParameterInfo parameterInfo)
{
ParameterInfo = parameterInfo;
}
public ParameterInfo ParameterInfo { get; }
public void Customize(IFixture fixture)
{
var mockedType = ParameterInfo.ParameterType.GetGenericArguments()[0];
fixture.Customizations.Add(new MockRelay(new ExactTypeSpecification(mockedType)));
}
}
You could then define an attribute that uses it
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class MockAttribute : Attribute, IParameterCustomizationSource
{
public ICustomization GetCustomization(ParameterInfo parameter)
{
return new MockCustomization(parameter);
}
}
and finally the test would look something like this
[Theory]
[AutoData]
public void DependencyShouldBeSameAsMockedObject(
[Frozen][Mock] Mock<DependencyClass> dependencyMock,
SystemUnderTest sut)
{
Assert.Same(dependencyMock.Object, sut.Dependency);
}
Closing the issue.
The question seems to have been answered.
Looks great thanks.
@aivascu do you think the types above could be incorporated in the AutoMoq library?
They seem generic and general purpose enough to deserve the honour.
@Kralizek yes it might be useful to have it in the AutoMoq lib.
I'll add an issue to the backlog, with a proposal for this feature.
Finally had a chance to test it and it works great.
I made a minor improvement (I know its still not robust enough for all uses) so that you can still request the concrete object instead of the Mock wrapper.
public void Customize(IFixture fixture)
{
Type? mockedType = (typeof(IMock<object>).IsAssignableFrom(ParameterInfo.ParameterType))
? ParameterInfo.ParameterType.GetGenericArguments()[0]
: ParameterInfo.ParameterType;
fixture.Customizations.Add(new MockRelay(new ExactTypeSpecification(mockedType)));
}
Allows
[Theory]
[AutoData]
public void DependencyShouldBeSameAsMockedObject(
[Frozen][Mock] DependencyClass dependencyMock,
SystemUnderTest sut)
{
Assert.Same(dependencyMock, sut.Dependency);
}
@Kralizek yes it might be useful to have it in the AutoMoq lib.
I'll add an issue to the backlog, with a proposal for this feature.
Since I recently stumbled upon this very issue, do you mind me putting forward a PR to implement the MockAttribute as you specified?
@aivascu
@Kralizek yes please do if you have implemented it. AFAIR when I attempted to implemented it wasn't as trivial as the implementation from above. Lets move the discussion to #1269 .
Most helpful comment
Finally had a chance to test it and it works great.
I made a minor improvement (I know its still not robust enough for all uses) so that you can still request the concrete object instead of the Mock wrapper.
Allows