-
-
Notifications
You must be signed in to change notification settings - Fork 812
Moq.Protected.Setup() Breaks On Abstract Class with Overloaded Abstract Methods #735
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
Comments
Hi @sdepouw. Thanks for reporting this. I'll take a look at it during the next few days. Stay tuned! |
I haven't done any real analysis yet, but I've tracked this down to commit c8c5db1. |
Actually looking at the commit I've referenced, things look quite simple: Before that commit (which was the last commit before The reason why
Since you're setting up a method with a return type of We should add a new overload |
@sdepouw - Would you be interested in contributing this addition? (If so, here's a rough summary of what would need to be done:
|
Btw. if you need another workaround that doesn't require you to change // In your test project, define an interface describing the protected members of your actual class.
// Note that your actual class does *not* need to implement this:
interface IMyAbstractClassProtectedMembers
{
void ApplyRule(IDictionary<string, object> tokens);
}
// Then use that interface as follows to set up `MyAbstractClass.ApplyRule`:
var mockedRule = new Mock<MyAbstractClass>();
mockedRule
.Protected().As<IMyAbstractClassProtectedMembers>()
.Setup(m => m.ApplyRule(It.IsAny<IDictionary<string, object>>()))
.Callback(() => _timesApplyRuleCalled++);
// (The rest of your code stays the same.) ( |
I have the same issue with
I am trying @stakx's work around but it's not working with our setup for some reason, probably my fault, I'll update here if I get it to work. |
@Shereef, if you have a small repro code example, feel free to share it. Might look like the same problem, but have different causes. |
@stakx: the problem is that GetMethod is looking for compatible params and my params inherit from eachother and so the parent of the class has a Populate with 1 param that is the parent of the subclass Populate param so it's compatible so SingleOrDefault throws the exception mentioned above. I don't have an example readily available but I have done my research and this is I have come up with. Until this but is fixed I am using inheriting Mock and adding thise method:
It doesn't verify the arguments but my case doesn't require that. |
@stakx I didn't know it would show up here but my commit on my fork adds test cases for it Shereef@9aee4d2 please clone and run it, I wanted to do it cleanly but I had to add a nuget to access the protected method because I didn't know how to otherwise do it without that nuget I hope this helps, P.S. I pulled latest and this test still doesn't pass It fails on the setup and if we make call base true it will fail on the verify too |
Added another case that actually breaks on Verify Hope this helps! |
@Shereef - Thanks for the pointer to your tests! Regarding Shereef@9aee4d2:
Using that NuGet package is one option, but since we're in a test setting where we have control over (most of) our test types, it is probably easier to simply add public proxy methods to the test types that invoke their protected counterparts: public class Parent
{
- protected bool Populate(ref ParentDto dto)
+ protected virtual bool Populate(ref ParentDto dto) // see comment below
{
return true;
}
+
+ public bool InvokePopulate(ref ParentDto dto)
+ {
+ return this.Populate(ref dto);
+ }
}
public class Child : Parent
{
- protected bool Populate(ref ChildDto dto)
+ protected virtual bool Populate(ref ChildDto dto) // see comment below
{
return true;
}
+
+ public bool InvokePopulate(ref ChildDto dto)
+ {
+ return this.Populate(ref dto);
+ }
} Then we can simply do this: -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject po = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(mock.Object);
-_ = po.Invoke("Populate", dto);
+mock.Object.InvokePopulate(ref dto); Since these public methods are non-virtual, they are unproxyable for Moq and therefore won't be recorded as invocations; so there's no danger that they'll affect the test result. Note that for the same reason just mentioned, your protected methods should be declared
It does, thank you! One problem is that we've fixed the The I'll look at your second test shortly. |
@Shereef, your second test (Shereef@27ca84b) fails for the same reason. Once there is a |
I've opened an issue about this (#752), so if you'd like to add any more information or test cases, I suggest we continue this discussion over there. |
I recently upgraded from Moq 4.5.28 to 4.10.1. After I performed the upgrade (via NuGet) and ran my tests, some failed.
I have the following abstract class:
And the following test fixture:
In 4.5.28, this worked fine. But now in 4.10.1, I get the following error:
I attempted to use the
exactParameterMatch
argument on theSetup()
method to see if that would help differentiate things:But that didn't seem to work:
My workaround was to rename one of the
ApplyRule
overloads to differentiate them in code. This makes Moq no longer confused (it doesn't find two methods with the same name). Based on other testing I did, it looks like this is becauseIDictionary<string, object>
can also beobject
. If I make an abstract class with, say,string
andint
, Moq has no troubles here.Is this a bug? Or is this a consequence of changes made to Moq that won't/can't be fixed?
The text was updated successfully, but these errors were encountered: