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

Implicitly implement substituted interfaces #250

Closed
jcansdale opened this issue Sep 22, 2016 · 5 comments
Closed

Implicitly implement substituted interfaces #250

jcansdale opened this issue Sep 22, 2016 · 5 comments

Comments

@jcansdale
Copy link

Hello,

Is there any way to implicitly implement the methods of substituted interfaces?

For example the following test current fails. Is there any way to make it pass without duplicating the Returns?

        [Test]
        public void ImplicitlyImplemented()
        {
            var mysp = Substitute.For<MyServiceProvider, IServiceProvider>();

            mysp.GetService(null).Returns("mysp");

            var isp = (IServiceProvider)mysp;
            Assert.That(isp.GetService(null), Is.EqualTo(mysp.GetService(null)));
        }

        public interface MyServiceProvider
        {
            object GetService(Type serviceType);
        }

It looks like the substituted class will look something like this:

        public class ServiceProvider : IServiceProvider, MyServiceProvider
        {
            object MyServiceProvider.GetService(Type serviceType) { ... }
            object IServiceProvider.GetService(Type serviceType) { ... }
        }

Unfortunately this make the substituting of some COM interfaces particularly awkward.

@dtchepak
Copy link
Member

Hi @jcansdale,

If these are the only two methods on the interface (or the only two that return object) you could try the ReturnsForAll extension. Bit of a hack but might work. Other than that, as far as NSubstitute knows these are two completely different methods so they'd generally need to be stubbed separately.

@jcansdale
Copy link
Author

Arg, that's a shame. I'm substituting some pretty big and complex interfaces. 😢

The trick I was using with TypeMock was to create an interface that implements all of my target interfaces and mock that. Something like this:

using EnvDTE;
public interface MyCodeClass : CodeClass, CodeElement, CodeType
{
}

Alas this doesn't work with NSubstitute and CodeClass, CodeElement, CodeType end up with separate implementations.

Apart from this the conversion has gone very smoothly! 😄

@dtchepak
Copy link
Member

Sorry the final mile hasn't been that smooth, but happy to hear things went pretty well in general. :)

If you need to do a lot of this you could try hacking something up with reflection.
For all members on IServiceProvider, delegate matching calls on ServiceProvider to that instance:

// Pseudocode
TTarget sub = ...
TDelegator delegator = ... 
foreach member of typeof(TTarget):
   if delegator has matching member and non-void:
     invoke member on delegator
     SubstituteExtensions.Returns(delegator, x => invoke member on sub)
  else if delegator has matching member:
    wire up to When..Do

This would be pretty terrible for test performance I'm guessing, but I think it would work.

I'm not sure how to support this feature properly from NSubstitute. :-\ I'm open to any ideas :)

@jcansdale
Copy link
Author

jcansdale commented Sep 26, 2016

@dtchepak,

Thanks for your reply. I just had another look to see what I could come up with. I've stumbled across a workaround that, while not pretty, isn't error prone like what I was doing before (defining returns individually for each interface).

If I define a new abstract class like this:

        public abstract class ACodeClass : CodeClass, CodeElement, CodeType
        {
        }

Ctrl-. offers an option that I'd never seen before: "Implement interface abstractly". If I do this and then substitute for this abstract class, the interfaces will be implemented implicitly. Although verbose, this works out fine. 😄

implement_interface_abstractly

Ideally this behavior would be the default when doing Substitute.For. Although I've know about explicitly implemented interfaces for a long time, I'm not sure I've ever seen them actually used in the wild. If it's tricky or potentially a breaking change, maybe the above workaround could be put in the FAQ? Substituting COM interfaces is a pretty great use-case and I expect this situation/gotcha is pretty common.

Thanks again for your work on NSubstitute. It really has been a pleasure to use. 😄

BTW, I've been working on a library for mocking static methods, that has ended up integrating perfectly with NSubstitute. If you're interested, I've put a simple getting started page up here:
https://github.com/jcansdale/staticmocks

@dtchepak
Copy link
Member

dtchepak commented Oct 6, 2017

Closing as part of a general cleanup of issues. Please re-open if any action is required.

@dtchepak dtchepak closed this as completed Oct 6, 2017
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