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

Provide fluent interface #833

Closed
mu88 opened this issue May 28, 2019 · 7 comments
Closed

Provide fluent interface #833

mu88 opened this issue May 28, 2019 · 7 comments

Comments

@mu88
Copy link

mu88 commented May 28, 2019

It would be great if we could write something like the following:

IMyType myMock = new Mock<IMyType>().Setup(...).Returns(...)
                                    .Setup(...).Returns(...)
                                    .Object;
@stakx
Copy link
Contributor

stakx commented May 30, 2019

Hi @mu88, thanks for your suggestion. As this would be a fairly fundamental change to Moq's fluent API, we'll have to check with @kzu whether this would also stand a chance of being included in the upcoming Moq v5. (It would be a pity to do all the work to add it in v4 only to once again have it lacking in v5.)

My initial reaction here is somewhat ambivalent:

  • From a user perspective, this would be a nice shortcut in a few use cases.

  • Often however, you can already use Mock.Of<T> to achieve much the same thing in an even terser fashion.

  • This would blur the line of when the fluent API is configuring a setup, and when it's configuring the mock. For better ease of understanding the API, it might be better not to mix up these two concerns.

  • This will require adding .Setup* verbs at every step of the fluent API, which means quite a bit of code duplication and added maintenance effort. (It's simple code, but repetitive and quantitatively quite a lot of it.). I secretely hope we can avoid that.

Any thoughts or opinions?

@kzu
Copy link
Member

kzu commented May 30, 2019

If all you're doing are Setup...Returns, you could just use Linq to Mocks instead:

IMyType my = Mock.Of<MyType>(x => x.Foo == ... && x.Bar == ...);

As @stakx said already, of course :)

@mu88
Copy link
Author

mu88 commented Jun 3, 2019

Thanks to the two of you for sharing your ideas :) I absolutely understand your concerns.

I didn't know about Mock.Of<>() and will take a closer look on it! Just two thoughts/observations about this:

  • I tried to use it for an async method in the way of Mock.Of<IMyType>(async x => await x.DoSomethingAsync() == new CustomResult()), which cannot be compiled.
  • At a first look, the usage of equality operator == instead of assignment operator = within the lambda is a bit counterintuitive. I will see whether it confuses my colleagues ;)

@stakx
Copy link
Contributor

stakx commented Jun 3, 2019

I tried to use it for an async method in the way of Mock.Of<IMyType>(async x => await x.DoSomethingAsync() == new CustomResult()), which cannot be compiled.

The C# compiler has some unfortunate limitations of what it can convert to LINQ expression trees. (See dotnet/csharplang#158.)

You could work around it as follows:

-Mock.Of<IMyType>(async x => await x.DoSomethingAsync() ==                 new CustomResult() )
+Mock.Of<IMyType>(      x =>       x.DoSomethingAsync() == Task.FromResult(new CustomResult()))

At a first look, the usage of equality operator == instead of assignment operator = within the lambda is a bit counterintuitive. I will see whether it confuses my colleagues ;)

It can be helpful to think about Mock.Of<T>(expr) in a declarative (rather than imperative) way: "Give me a mock that meets these conditions: <expr>."

@kirinboy
Copy link

Could Mock.Of<T> have a fluent API? If I want to mock multiple properties or methods, it is not very clear to read and write.

@stakx
Copy link
Contributor

stakx commented Jun 11, 2019

@kirinboy, a fluent API wouldn't work with Mock.Of<T> since it returns the mock object instance directly, which can have any type. Just use && (as shown in an above post) to set up several members.

@stakx
Copy link
Contributor

stakx commented Jul 24, 2019

I'm closing this issue, seeing that there is an existing & mostly viable alternative (Mock.Of<T>(predicate)) to what was requested here. If such a fundamental change should be made to the setup API, I'd prefer to let Moq v5 take the lead here.

That being said, I'll think about some easy and inconspicuous extension points in the fluent API that would allow client code to add their own extension methods to make this work. (More specifically, if the interfaces making up the fluent setup API each made their current "context" accessible, e.g. via some Mock and Setup properties, it should become possible for client code to build new fluent APIs on top of that.)

(Exposing a Setup property would require us to expose a ISetup abstraction or a Setup base class, Moq v4 doesn't currently have something suitable for public consumption. Exposing setups for better introspection is on my private Moq v4 todo list, however.)

@stakx stakx closed this as completed Jul 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants