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

Mock an auto-derived interface with all getter-only properties having settings, too #1158

Closed
KarloX2 opened this issue Apr 29, 2021 · 4 comments

Comments

@KarloX2
Copy link

KarloX2 commented Apr 29, 2021

Hello,

suppose I have lots of interface each with many get-only properies like this:

public interface IMyDto {
  int Value { get; }
}

and what I like to have for my unit tests is just a mocked object instance that allows me to set a fixed value for properties like Value. Well, I can achieve it by doing something like:

var dto_mock = new Mock<IMyDto>();
dto_mock.SetupGet(m => m.Value).Returns(42);

But it would be much easier to have something like this:

var dto = Mock.Of<IMyDto>(withAddSetters: true); //this could auto-derive an interface IMyDtoEx or so
dto.Value = 42;

Makes sense? Possible?

Thanks!

@stakx
Copy link
Contributor

stakx commented Apr 29, 2021

No, sorry, this isn't feasible. Moq creates objects at run-time; it cannot create new static types at compile-time, so the best we can do is to make the C# compiler think that your mock has the same static type as the mocked type. It's impossible to magically add members to that static type.

Nothing can be done here, so I'll close this.

P.S.: Why do you need a setter anyway? Why not simply let the getter return the value you want? Mock.Of<IMyDto>(m => m.Value == 42)

@stakx stakx closed this as completed Apr 29, 2021
@KarloX2
Copy link
Author

KarloX2 commented Apr 29, 2021

@stakx Thanks for you comment. I'm new to Moq and just start to understand how it works. I wasn't aware of the expression syntax you mention in you PS. I guess it derives the setups by inspection the expression tree, is that correct? This is more handy than doing the .SetupGet() calls.

One more question: Suppose my IMyDto interface has not only one but 20 getterts and I'm happy with how I equipped all the get-properties on a first instance. Then I'd like to change only one out of the 20 getters, say to return 43 instead of 42 on the next get attempt but leave all the others unchanges. Can I modify / override / exchange that single value somehow without setting up a brand new instance or clone from the existing with just one change?

Thanks!

@stakx
Copy link
Contributor

stakx commented Apr 29, 2021

Can I modify / override / exchange that single value somehow without setting up a brand new instance or clone from the existing with just one change?

Yes. Simply set up the same property again. Moq prioritises newer setups over older ones, so if you have two setups for the same property, the second one "wins".

// initial setup:
var twentyProperties = Mock.Of<IHaveTwentyProperties>(x => x.One == "1"
                                                        && x.Two == "2"
                                                        && ...
                                                        && x.Thirteen == "13"
                                                        && ...);
Assert.Equal("13", twentyProperties.Thirteen);

// do stuff with it:
...

// modify existing mock by "overriding" one of its setups:
Mock.Get(twentyProperties).SetupGet(x => x.Thirteen == "danger! a black cat just passed under the ladder!");
Assert.Equal("danger! a black cat just passed under the ladder!", twentyProperties.Thirteen);

(Incidentally, this shows that you can mix and match the declarative Mock.Of<> API with the more imperative Setup... calls. Mock.Of<T> returns a T, you can get back to a Mock<T> via Mock.Get() and from there do Setup... calls.)

That being said, you can take things too far. If you modify a mock so often that you're no longer sure how it will behave, your test code might become fragile. You might be better off starting with a fresh mock each time around, using a parameterized helper/factory method:

IHaveTwentyProperties PrepareTwentyProperties(string thirteen)
{
    return Mock.Of<IHaveTwentyProperties>(x => x.One == "1"
                                            && x.Two == "2"
                                            && ...
                                            && x.Thirteen == thirteen
                                            && ...);
}

var twentyProperties = PrepareTwentyProperties("13");
...
twentyProperties = PrepareTwentyProperties("danger! a black cat just passed under the ladder!");

That way your test code will have less logic, and thus fewer things that can go wrong.

@KarloX2
Copy link
Author

KarloX2 commented Apr 30, 2021

Thank you very much for this detailed explanation!
I'm sure I will be able to solve it base on your recommendations.

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