New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework integration with Moq to support generic methods #1139
Comments
public class ValueProviderTests
{
[Fact]
public void UseValueProvider()
{
var fixture = new Fixture();
var mock = new Mock<IInterfaceWithGenericMethod>
{
DefaultValueProvider = new AutoFixtureValueProvider(fixture)
};
Assert.NotNull(mock.Object.GenericMethod<string>());
}
[Fact]
public void CustomizedAnonymousType()
{
var fixture = new Fixture();
fixture.Customize<string>(x => x.FromSeed(y => "asf"));
var mock = new Mock<IInterfaceWithGenericMethod>
{
DefaultValueProvider = new AutoFixtureValueProvider(fixture)
};
Assert.Equal("asf", mock.Object.GenericMethod<string>());
}
public class AutoFixtureValueProvider : DefaultValueProvider
{
private readonly IFixture _fixture;
public AutoFixtureValueProvider(IFixture fixture)
{
_fixture = fixture;
}
protected override object GetDefaultValue(Type type, Mock mock) =>
_fixture.Create(type, new SpecimenContext(_fixture));
}
} This looks promising, except for the fact the mock is still tight to the interface. However, I expect most of reflection code could be replaced by this. |
@riezebosch: Looking good. Btw., if you subclass |
@zvirja: Thanks for the reminder! Someone noticed a while ago over at Moq's repo that due to the accessibility of Also, if anyone here finds other problems with Moq's |
Checkout my work on the value provider: https://github.com/riezebosch/AutoFixture/tree/value-provider. What's not working (yet):
Had to use reflection to invoke the Regarding the [Fact]
public void RefParameter()
{
var mock = new Mock<IInterfaceWithRefMethod> { DefaultValueProvider = new TestValueProvider("bye") };
var s = "hi";
mock.Object.Method(ref s);
Assert.Equal("bye", s);
}
[Fact]
public void OutParameter()
{
var mock = new Mock<IInterfaceWithOutMethod> { DefaultValueProvider = new TestValueProvider(4) };
mock.Object.Method(out var s);
Assert.Equal(4, s);
}
private class TestValueProvider : DefaultValueProvider
{
private readonly object _result;
public TestValueProvider(object result) => _result = result;
protected override object GetDefaultValue(Type type, Mock mock) => _result;
}
So it looks like |
As far as I can tell, from Moq's perspective, that's by design:
That's why the default value provider doesn't get queried for those by-refs. If AutoFixture always sets by-ref parameters, then Moq and AutoFixture have different modes of operation, and I'm not yet sure how we can seamlessly bring the two together.
Yes, Moq doesn't have a non-generic public API, it's in the planning however (see e.g. devlooped/moq#887). For the moment, I'm afraid you'll need that reflection detour. |
Minor correction, for the For some reason the ref methods were ignored in current implementation so I guess this is already a step forward. |
@zvirja Please, checkout https://github.com/riezebosch/AutoFixture/tree/value-provider. Made quite some progress and was able to remove a lot of code. I did some tweaks to make it running on my macbook, the actual implementation and the removal of code in separate commits. Let me know what you think. |
Strange... none of what you've described should happen. May I ask you to post a small demo code—either here or (perhaps better, so we don't hijack this issue) over at the moq/moq4 repo? |
It's in my reply above: #1139 (comment) |
I ran those tests, which fail (as expected).
I tried to reproduce that claim, but failed. I made the following change: private class TestValueProvider : DefaultValueProvider
{
private readonly object _result;
public TestValueProvider(object result) => _result = result;
- protected override object GetDefaultValue(Type type, Mock mock) => _result;
+ protected override object GetDefaultValue(Type type, Mock mock) => throw new InvalidOperationException("unexpectedly invoked default value provider");
} But that exception never gets thrown by the two tests, meaning |
My guess is the exception gets swallowed by Moq. I slightly modified the provider: [Fact]
public void RefParameter()
{
var provider = new TestValueProvider("bye");
var mock = new Mock<IInterfaceWithRefMethod> { DefaultValueProvider = provider };
var s = "hi";
mock.Object.Method(ref s);
Assert.True(provider.IsInvoked);
Assert.Equal("bye", s);
}
[Fact]
public void OutParameter()
{
var provider = new TestValueProvider(4);
var mock = new Mock<IInterfaceWithOutMethod> { DefaultValueProvider = provider };
mock.Object.Method(out var s);
Assert.True(provider.IsInvoked);
Assert.Equal(4, s);
}
private class TestValueProvider : DefaultValueProvider
{
private readonly object _result;
public TestValueProvider(object result) => _result = result;
protected override object GetDefaultValue(Type type, Mock mock)
{
IsInvoked = true;
return _result;
}
public bool IsInvoked { get; set; }
}
moq 4.13.0 BTW. |
@riezebosch, I cannot reproduce your public interface IInterfaceWithRefMethod { void Method(ref string arg); }
public interface IInterfaceWithOutMethod { void Method(out int arg); } (Perhaps your |
You're right, the value provider is visited for the ref parameter method but only because the interface method is defined as |
This issue is to track our activities regarding rework of Moq integration. Currently we are creating
Mock<T>
using reflection and then configure each method individually using reflection. Due to this approach we have a limitation and do not support generic methods, as we cannot configure all possible instantiations upfront.We should change our integration approach and instead register AutoFixture as default value provider. This way we inject "passively" and are invoked by Moq when value is required. We are already following that way with NSubstitute and it works pretty well.
We haven't investigated yet whether it's possible, but @stakx promised to give it a look one day.
Let's postpone this activity till the moment we start our work on v5, as this change might require us to change minimum supported version for Moq. Unless somebody wants to look upfront to prepare Moq for this.
I'll update this issue once we start work on v5.
The text was updated successfully, but these errors were encountered: