Moq4: Mock with an instance callback?

Created on 9 Jul 2020  Â·  6Comments  Â·  Source: moq/moq4

Hi,

Here's what my test constructor looks like:

```c#
public SynonymServiceTests()
{
this.storageBrokerMock = new Mock();
this.dateTimeBrokerMock = new Mock();
this.loggingBrokerMock = new Mock();
this.validatorMock = new Mock>(this.dateTimeBrokerMock.Object);

this.synonymService = new SynonymService(
    storageBroker: this.storageBrokerMock.Object,
    dateTimeBroker: this.dateTimeBrokerMock.Object,
    loggingBroker: this.loggingBrokerMock.Object,
    synonymValidator: this.validatorMock.Object);

}


This thing is, that the `IValidator<Synonym>`, has a constructor parameter of `IDateTimeBroker`, which I need it to be `dateTimeBrokerMock.Object`.

Ideally, what I'm looking for is an ability to setup the instantiation of a mocked object, via a callback that will be invoked when that mock's `Object` property is called.

```c#
var dateTimeBroker = dateTimeBrokerMock.Object;
this.validatorMock = new Mock<IValidator<Synonym>>(() => new SynonymValidator(this.dateTimeBroker.Object));
question

Most helpful comment

@weitzhandler You can't have a ctor in a C# interface. Only a class derrived from the interface can have a ctor. I guess that is what stakx is trying to tell you with "I am going to assume that despite its name prefix, IValidator<> is actually not an interface but a class type, otherwise passing ctor arguments to the Mock<> ctor would be invalid."

All 6 comments

I am going to assume that despite its name prefix, IValidator<> is actually not an interface but a class type, otherwise passing ctor arguments to the Mock<> ctor would be invalid.

If your object model requires that IValidator<> is given an already-instantiated dependency, then the same requirement will hold with the new Mock<IValidator<...>> ctor. You can change your IValidator<> ctor such that it accepts a Func<IDateTimeBroker> (or some other factory function/type) instead of a IDateTimeBroker.

Looking at this from the side of Moq, it doesn't make much sense to put a whole lot of work into additional ctor argument validation & processing just to enable this edge case. You should probably just solve this on the side of your own code, as laid out above.

IValidator<T> is an interface.
Fortunately it doesn't have methods I need to set up for mocking, so I was good with creating by hand, but having the ability to provide a mocked instance via a callback on demand would be a great add.

Anyway, thanks for your reply.

@weitzhandler You can't have a ctor in a C# interface. Only a class derrived from the interface can have a ctor. I guess that is what stakx is trying to tell you with "I am going to assume that despite its name prefix, IValidator<> is actually not an interface but a class type, otherwise passing ctor arguments to the Mock<> ctor would be invalid."

@siprbaum, yes, that's what I wanted to say with that sentence. Thanks for clarifying.

I see.
I saw it before, but I now understand.
IValidator<T> is indeed an interface, but I failed to understand the logic behind this, which I think I got now.

If I want to use a concrete type, I'm not supposed to mock its methods, and if it's an interface type, I'm not supposed to have concrete methods but set-up ones, so what I'm asking here indeed doesn't really make sense.

Thank you guys for your extended patience clarifying that.
And it's also my chance to thank you for this awesome library ♥

If I want to use a concrete type, I'm not supposed to mock its methods, and if it's an interface type, I'm not supposed to have concrete methods but set-up ones, so what I'm asking here indeed doesn't really make sense.

Hmm, that still doesn't seem quite right.

What I said above was only about constructors: It doesn't make any sense to pass constructor arguments to an interface mock, because what is Moq supposed to do with those arguments? Interfaces don't have any constructors to which they could be passed. Contrast that with class mocks; classes do have one or more constructors, and Moq will simply forward the arguments you provided to one of them.

Whether you should provide setups for your mock's methods or not does not depend on the mocked type (interface vs class) — the important thing here is how you want your mock to behave:

  • With loose mocks (which is the default), any method you call on your mock will simply do nothing and return the default value. That's OK if you don't care about that method, e.g. because you don't expect anyone to actually call it. If you want a mock method to do something, or return a non-default value, you need to create a setup for that method.

  • With strict mocks (which you get when specifying behavior: MockBehavior.Strict during mock creation), calling any methods on your mock will immediately throw, unless you've explicitly set them up beforehand.

class types don't just declare methods (like interfaces); they can also define / implement their behavior. So if you're mocking a class type, and set up a method, you can tell Moq to use the mocked class' method implementation by specifying .CallBase(). If you want that to be the default behavior for all method calls on a mock, set mock.CallBase = true. Both of those CallBase mechanisms only make sense with mocked classes; interfaces usually don't provide any method implementations.*


(*) This statement is no longer true starting with C# language version 8, where interfaces can have default method implementations. However, at this time of writing, Moq does not yet support those.

Was this page helpful?
0 / 5 - 0 ratings