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
Feature: Extend PartsOf to mock non-virtual methods implementing an i… #700
base: main
Are you sure you want to change the base?
Conversation
Nice feature to test the examples in the documentation. 😄 There was an use case in the docs that was not in the code, in the file There was a regression because the new ForPartsOf is based on For<>, and there was a new check to ensure that the class implemented the interface. It did not break any of the existing tests but it did with those on the documentation. I have solved it and copied the test into the solution. Now |
302440e
to
a04dc32
Compare
I've found that the use case mentioned in my previous comment and taken from the docs was not working ok for all scenarios. In the case of partial substitutes, the substitute can't extend the base class, just the interface, since we are substituting non-virtual methods. I think this is an acceptable limitation. In my previous implementation, regretfully, this was happening for all other substitutes. While I believe it is an edge case, it would have meant a breaking change. The new implementation involves passing a parameter around to know when we are creating a partial substitute or not. I find this a bit inelegant, but it causes less friction with the current code. I've removed the previous commits and replaced them with a squashed version. Relevant tests are in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay in reviewing this. 😓 🙇
This is a really nice PR, thanks! I'm wondering if, to be extra careful, we should leave the existing Substitute.ForPartsOf
unchanged, and expose this functionality through Substitute.ForTypeForwardingTo()
or similar? That way changes to the types passed will not result in significant changes to how the resulting substitute behaves. WDYT?
src/NSubstitute/Proxies/CastleDynamicProxy/CastleDynamicProxyFactory.cs
Outdated
Show resolved
Hide resolved
src/NSubstitute/Proxies/CastleDynamicProxy/CastleDynamicProxyFactory.cs
Outdated
Show resolved
Hide resolved
Hi!
Thank you for taking the time to review the PR. I'm glad you like it. I
will make the amendments you suggested.
|
Hi again I've added the changes you suggested:
Please, kindly let me know if you find something else to improve. |
I would love to have this integrated in NSubstitute... @marcoregueira - thank you for your work on this! |
Thanks @marcoregueira for your solution, it's something I look forward to have in NSubstitute. |
Sorry, this completely slipped off my radar. 😞 🙇 Before I merged I wanted to add some tests to demonstrate equivalence between substitutes created using this method vs. If anyone has time to look at that I would greatly appreciate it, otherwise thanks a lot for the reminder and I'll try to look into this again shortly. |
…nterface. How to use: var substitute = Substitute.ForPartsOf<ISomeInterface,SomeImplementation>(argsList); In this case, it doesn't matter if methods are virtual or not; it will intercept all calls since we will be working with an interface all the time. Limitations: Overriding virtual methods effectively replaces its implementation both for internal and external calls. With this implementation Nsubstitute will only intercept calls made by client classes using the interface. Calls made from inside the object itself to it's own method, will hit the actual implementation.
…ods or sealed classes implementing an interface. How to use: var substitute = Substitute.ForTypeForwardingTo <ISomeInterface,SomeImplementation>(argsList); In this case, it doesn't matter if methods are virtual or not; it will intercept all calls since we will be working with an interface all the time. For Limitations: Overriding virtual methods effectively replaces its implementation both for internal and external calls. With this implementation Nsubstitute will only intercept calls made by client classes using the interface. Calls made from inside the object itself to it's own method, will hit the actual implementation.
e88249f
to
05cb82c
Compare
Thank you for your review. I've upgraded the code to the latest version and made the necessary updates to the PR. To verify the new feature, please refer to the tests in |
Hi
I was in need of a feature allowing PartsOf to mock non-virtual methods implementing an interface, just the same as requested in #625.
I decided to tinker a bit to see If there was any way of doing it. Solution came out faster than expected and here are the results. I thought you might find it useful enough to include it.
In short, Castle already provides this feature and the task was, mostly, opening a way to expose it without breaking anything.
How to use:
var substitute = Substitute.ForPartsOf<ISomeInterface,SomeImplementation>(argsList);
var substitute = Substitute.ForTypeForwardingTo<ISomeInterface,SomeImplementation>(argsList);
Tests are included, of course, to see it in action.
Obvious limitations:
Overriding virtual methods effectively replaces its implementation both for internal and external calls. With this implementation Nsubstitute will only intercept calls made by client classes using the interface. Calls made from inside the object itself to its own method will hit the actual implementation.
Possible improvements
The next logical improvement is to provide mocking for already created objects conforming to an interface. Something like this:
Please kindly take a look and give your feedback! Thanks!