Skip to content
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

Problem when mocking an interface with hidden property of different type #930

Closed
angelaki opened this issue Sep 20, 2019 · 5 comments
Closed

Comments

@angelaki
Copy link

angelaki commented Sep 20, 2019

I get an "AmbiguousMatchException" when mocking this interface:

public interface A { string Prop { get; } }
public interface B : A { new int Prop { get; } }

[Fact]
public void TestMock()
{
	var mock = new Mock<B>();
	var p = mock.Object.GetType().GetProperty("Prop");
}

If the "Prop" types match it works. But what to do in this case? In my real scenario the implementation of the interface explicitly describes the value of the two properties. Their names need to match.

@stakx
Copy link
Contributor

stakx commented Sep 20, 2019

This is not directly related to Moq, it's a basic question about .NET reflection. If you have two properties with the same name, you need to specify its parameter & return types with GetProperty, or otherwise use GetProperties instead.

@stakx stakx closed this as completed Sep 20, 2019
@angelaki
Copy link
Author

Yes, I know. But for example EF uses reflection to set values of an entity based on a poco:

Entry(paramEntity).CurrentValues.SetValues(mock.Object);

Would throw this exception. How to deal with it? I know it is not absolutely moq-related. But doesn't moq need to offer a solution for this?

@stakx
Copy link
Contributor

stakx commented Sep 20, 2019

No, Moq is not a library for doing reflection.

Your issue is with a .NET reflection method call, so you need to learn how to use that API before anything else.

@angelaki
Copy link
Author

angelaki commented Sep 20, 2019

No, the problem is in your Proxy instance using the DynamicProxy. You should be able to override a method that choses the property that needs to be taken.

E.g. in this case I could just say "GetProperties().First(p => p.Name == name)" or I'd know what property of what interface interests me. This fix can only be done in and by Moq I guess?

@stakx
Copy link
Contributor

stakx commented Sep 20, 2019

the problem is in your Proxy instance using the DynamicProxy.

(In that case, you're at the wrong repository. If you feel DynamicProxy is misbehaving, you should make a report over at castleproject/Core.)

DynamicProxy will create a proxy type similar to the following:

partial class BProxy : B
{
    public string Prop { get { ... } }
    public int Prop { get { ... } }
}

which isn't valid C#, because C# does not allow you to overload members that differ only by their return types. But that practice is perfectly legal and possible at the IL / runtime level, and DynamicProxy happens to take advantage of that.

DynamicProxy makes no promises about how the generated proxy types will be implemented except that they're valid CLR types and that they can be used as proxies for the proxied type(s) (which in practice means that the proxy types are either subclasses of the proxied class type, and/or they implement the requested interfaces).

Therefore, the real issue here is that you are making assumptions about the proxy types that go beyond what you're promised by DynamicProxy and Moq.

Let me give you another, very similar example that takes both DynamicProxy and Moq out of the equation: Say you implemented the proxy type manually as follows:

class BProxy : B
{
    string A.Prop { get; }
    int B.Prop { get; }
} 

Note that this is a perfectly legal implementation of B.

Now say you'd go ahead and fetch the property (as you've tried above):

var mockObject = new BProxy();
var p = mockObject.GetType().GetProperty("Prop");  // returns null

Again, you'd be making an assumption about the proxy type that doesn't hold in this case; specifically, there is no single public property called Prop.

To summarise, the assumption embedded in your reflection query simply won't hold in all cases. One of those being the manually defined BProxy above, another being the proxy type created for you by DynamicProxy at runtime.

Of course you can go and submit an issue and/or PR against DynamicProxy (good luck with that), but you could also simply adjust your expectations and rewrite your reflection query such that it is more robust and makes less assumptions about the proxy types.

This fix can only be done in and by Moq I guess?

No, as mentioned above, the proxy types used by Moq are generated by DynamicProxy, that's where any change would have to happen. But frankly, no fix is necessary because nothing is broken in this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants