To have an interface Mock use existing implementation logic, devs should be able to provide an implementation class #95
Replies: 6 comments
-
Sorry for the labelling confusion, wrong repo. :) |
Beta Was this translation helpful? Give feedback.
-
I posted a comment earlier, erroneously thinking this was about Moq 4, so I deleted it. I'd still like to mention that even with Moq 5, this could result in a feature leading users into hard-to-understand situations: there will no longer be just one mock object, but also another concrete instance which is completely outside of Moq's control. Moq will therefore have a "blind spot" with such mocks, and you could run into confusing situations due to state split across the two objects. (The DynamicProxy team has some experience with that, as DynamicProxy has a feature similar to what is being asked here: interface proxies with a target. The "split state" issue is a real point of confusion there.) We have another area in Moq where users have run into confusing situations in the past: class proxies. Likewise, these have "blind spots" in the form of non-virtual and sealed methods. My tendency is to advise against putting that new feature into the core library, even though it might indeed come in handy from time to time. |
Beta Was this translation helpful? Give feedback.
-
No worries, I almost posted this request in the Moq4 repo myself, I was just lucky and spotted the new work going in Moq 5 repo note just before I hit submit.
Agreed, I would never consider allowing this kind of behaviour for anything that's not an interface (which is by definition 100% abstract). But - I do concede the point about split state. Even on a simple scenario like this could be challenging: public interface IFoo
{
int TheCount { get; set; }
// Concrete impl for this is simply: return TheCount > 5;
bool IsCountMoreThanFive();
} Now I mock that, with a concrete impl. But I use
Hehe, it's partly because I know about interface with target proxies that I asked for this, since I had a feeling that Moq has used DynamicProxy under the hood for some time 😄 Overall, I'd say I agree with you. Whilst it's a bit of functionality that I would like from time to time, it's also an awfully large gun that is very easy to aim at one's own foot. My workaround currently is to use explicit setups for functionality which I'd like to delegate to a concrete impl (via things like Perhaps if I get sufficient time (and ever feel this becomes a priority, rather than just using that workaroud) then I'll put something together of my own to cover this as some kind of extension library. Does Moq (4 or 5) permit any way of getting at the underlying dynamic proxy which backs the mock object? So that extenders could add their own interceptors or the like? |
Beta Was this translation helpful? Give feedback.
-
@craigfowler, I cannot say for Moq 5 (which AFAIK does not use DynamicProxy, at least not by default; as I understand it, it's based on Roslyn & compile-time code generation instead). For Moq 4, while DynamicProxy is used under the hood, that fact isn't exposed in the public API, nor is it guaranteed. Factually though, a |
Beta Was this translation helpful? Give feedback.
-
It's occurred to me that in Moq 4, it's already possible to do this - at least in the scenario I was thinking of. The usage would be something like this: IMyService GetService()
{
var mock = new Mock<MyServiceImpl> { CallBase = true; };
mock.As<IMyService>();
return mock.Object;
} At that point if I use the object without performing setups, I'll get the functionality from the selected impl. I can still perform setups on it though (via I don't even need the functionality on the concrete impl to be virtual (EDIT: I realise that the class mustn't be sealed, for this to work), because when I'm performing setups, I'm doing it "as the interface" and not as the concrete impl, so DynamicProxy doesn't scream at me for trying to intercept non-virtual members. So - actually - as long as Moq 5 doesn't remove the ability to do this, I'd still be quite happy. |
Beta Was this translation helpful? Give feedback.
-
Hi there! Sorry for the LOOONG time to catch-up to feedback on vNext. I'm only recently back to speed working on it. For vNext (a from-scratch revamp) there is an underlying Moq.Sdk that in turn uses an underlying entirely new interception library (https://github.com/devlooped/avatar) which gives you complete access to the underlying primitives in a first-class supported way. In fact, the Moq API is implemented using only publicly available extensibility APIs provided by the Moq.Sdk (which is in turn based on public extensibility from Avatar). Avatar replaces DynamicProxy (and decouples it from whether you're doing compile-time or run-time proxy generation), and the Moq.Sdk makes the internals of the mocking library a public extensibility API. When you create a new mock now (with The Moq API already configures some built-in behaviors in the underlying proxy, in particular the last two are the ones that would kick-in for strict behavior (throwing for things that aren't set up) and loose mocks (returning default values). Depending on your desired behavior, you can insert your own behavior anywhere in that behavior pipeline to cause invocations to flow elsewhere. What your proposed behavior would look like in practice using the new APIs is something like this:
(note that this sample adds your default impl. just before either the strict or the default behavior mock, otherwise, at the end of the pipeline). Your implementation of
I added a few sample behaviors to showcase what's possible. Any stunt behavior can also be added to any mock. Thanks @stakx for the background on the split state problem, I hadn't thought much about that, but indeed it sounds like it would be a tricky thing to support officially and document in a way that's easy to understand what's going on. |
Beta Was this translation helpful? Give feedback.
-
This is a feature request for new Moq functionality. Something which comes up from time to time in testing is a scenario when I want to create an interface-based Mock object and - by default - I want its functionality to behave as if it were a real instance of an implementation class for that interface. I don't want to just provide an instance of that class though, because I also want to use some of Moq's functionality such as writing one or more setups or using a
Verify
to verify that a method was called with the correct params.In this scenario what I would like would be a way to tell Mock:
In many ways, this is like setting
CallBase
to true for a class-based Mock, except that for interfaces, the developer needs to provide an impl. I'd expect to see it work in a very similar way; the matching functionality from the provided concrete impl would be executed and used, unless a setup matches the call to the mock.Suggested syntax
Here's some suggested syntaxes and overloads for the functionality. The names
UseDefaultImplementation
andUseDefaultImplementationType
are 100% up for grabs, I'm not precious about them at all.In this case/overload, the dev must pass an instance of
IFoo
to the mock object directly. This function could reasonably use a generic type constraint to ensure that the impl instance passed is of typeIFoo
.In this case/overload, the dev indicates the implementation type to use and Moq takes control of creating the instance, perhaps just via
Activator.CreateInstance
, or via a generic type constraint onnew()
. It would also be totally reasonable to have a generic type constraint onIFoo
here.This overload would be functionally identical to the above, except non-generic. Obviously, the compiler wouldn't be able to use generic type constraints to ensure you provided a sane type.
Beta Was this translation helpful? Give feedback.
All reactions