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
New lenient() strictness setting available at mock and at stubbing level #792
Comments
Thank you very much for this feedback! Do you use the silent JUnit Runner currently to avoid the exception in your scenario? Your feedback and recent code review from @bric3 made me think about enabling/disabling stubbing strictness per method or per class using annotations. For example (brainstorming): @Mockito(strictStubs = true)
public class SomeTest {
@Test
@Mockito(strictStubs = false)
public void someTestMethod() {
}
} |
Thanks for following up on this! Yes, we currently use the silent runner, which works fine, but of course lacks the stubbing checking that we would like to have on the rest of the methods. Annotations for this sounds to me like a good solution to the problem. It would be really great if this annotation could also be used on helper methods, such as in our scenario:
|
Thanks for the suggestions! My feedback / questions:
|
Thanks for your feedback!
I am not that into the Mockito codebase, but I would guess that this could require quite some rewrite? (And it also indicates that our usage of helper methods are probably not a smart path to follow...) |
My point on helper method or helper object for mocks is usually that the model shows mock anti pattern. Or that the granularity of the test is not narrow enough. Usually that means that a concrete object should be built instead, not a mock. The idea may be intersting though, probably not in mockito 2. In mockito 3 (JDK8) maybe we could introduce apis like this : mockitoRule.inConfiguration()
.strictness(Strictness.LENIENT)
.stub(() -> {
doReturn(codetype).when(code).getCodetype(); // possible stub
// ...
}); The above snippet is mostly exploring idea for this kind of API as I don't have these kind of needs at this time, it may be just wrong in many aspects. |
See #840 - I created a proposal for solving this use case for JUnit rules |
As an alternative solution, could you use the MockSettings? e.g. something like ..
|
I also found a use case: I'm mocking ZK which uses a lot of map-like structures which are exposed via accessors ( In my test setup, I create a map and I'm using
I need this code block three times (desktop attribute map, session attributes, servlet context attributes). I prefer mocking over mock objects since the interfaces have hundreds of methods. I have put this code into a shared JUnit rule. It's lazy but it's also generic: Since the rule doesn't know what parts of ZK the test will call, it can't tell which accessor methods will be needed. But I still need to know when a method is called which wasn't mocked. Therefore, I need a way to tell Mockito "there might be unused stubs in the following code block but that's ok since it's shared by many tests". An annotation on the test won't work since the code is in a JUnit rule. I could pass the MockitoRule to my rule, so this approach would work:
|
@digulla, thank you for reporting! Some feedback:
Ah, it was interesting to refresh my memory about this ticket :) Hope that helps! |
@mockitoguy Sorry, that doesn't help at all. With ~200K on StackOverflow, that was the most simple solution I could come up with. You're welcome to show me a better solution. The testing strategy is sound. It doesn't break easily or unexpectedly and is easy to understand for new members of the team. I could copy only the necessary lines to new tests but that would mean I would be the only one who can write new tests. Also: Violates DRY. Refactoring is not possible. ZK is a UI framework with a huge code base and many projects use it. Asking to change the API is like asking to "fix" the Java Collections API: Understandable, maybe even reasonable but unrealistic. Hand mocks would mean I would have to write about a lot of useless code which violates your own rule to keep tests simple. So I'm between a rock and a hard place: You're right for open source projects which no one uses or green field commercial projects. For existing commercial projects that I can't move, it's not helpful. They have ugly and unmodifiable APIs, so I have to move the only place where I have influence: That's the tests. So for now, I have to disable a good feature of Mockito and can no longer make sure that my tests stay clean. That really sucks. |
Thank you for describing your context! Let me think about this and I'll get back. |
I guess my use case is "I'm writing a mocking framework for some commercial API which helps other developers to write tests." That means I'll always overmock. In my case, I have to mock several Map-like APIs which are exposed in several beans without a common interface (just like the attribute maps in J2EE ServletContext and ServletRequest and the headers in ServletResponse). |
@digulla that pretty much matches my use case too. IMHO a good solution would be to be able to specify ‘Silent’ at mocked object level rather that the test class level. |
I like the idea of adding new public API for this use case. @digulla, with my earlier reply, I did not intend to depreciate your efforts in getting clean tests for your entire team. It’s great that you’re pushing for this! The use case you describe could be solved more cleanly with hand stubs. Sometimes simpler code is actually more code :) What do you think about this idea:
Coming back to Mockito for this use case. I bet that our users would disable strictness per entire test, rather than build hand stubs (even if hand stubs would be a better solution). Disabling strictness per entire test nullifies strictness benefits. To provide best dev experience, Mockito should honor this use case and offer a better API. There are 2 main alternatives:
mock(Foo.class, withSettings().strictness(Strictness.LENIENT));
mock(Foo.class, withSettings().lenient()); //alias
lenient().when(mock.foo()).thenReturn(“boo”);
lenient().doReturn(“boo”).when(mock).foo(); Any feedback? Thank you guys for feedback and for pushing us to reconsider. We're trying to keep Mockito API slim and avoid solving use cases better solved by refactoring / cleanup the code. |
I did wonder about something like ...
as well, but I wonder if that syntax would over-encourage people to use the feature; IME I think the sort of test frameworks, fixtures etc. where I see this to be most useful probably use the mock(Foo.class) mechanism for instantiating mocks. |
I see a third one. As discussed in the JUnit 5 PR (#1221) and the mockito google group I would like to propose an annotation base approach that can be applied also at test-class and test-method level. This keeps the API consistent and slim (at least when JUnit5 is used).
|
@ChristianSchwarz, you're right! Sorry for discounting the annotation option. |
I plan to tidy up this ticket in the next few days so that it documents the use case, the implementation options and the desired implementation. @ChristianSchwarz, strictness per method is not an ideal solution for the use case described in this ticket:
I must admit that the @strictness annotation does look handsome in the test :) What do you think about option 1 and 2? Do we want to implement both? Perhaps we start with mock level annotation for now. It solves the use case very well and is not very controversial. |
Thats why I proposed to use the The
I would prefer option 1 cause:
Option1 +
|
That's fair! Let's discuss the 2 contention points:
|
@mockitoguy Re "hand stubbed": That doesn't work since Java doesn't allow multiple inheritance. I just pasted a small part of the actual test setup. My setup class can mock several aspects of the ZK API: Session and request attributes, event queues, view models, injection. What I end up with a mix of lenient and dedicated mocks. With a hand stubbed approach, I would have to copy a lot of code from test to test. My goal is to have lenient mocks for unimportant parts like session attributes (code under test will complain when they are missing) but strict checking for things like events published (I really don't want to miss those). The test can then say "hey, wire up event publishing" if they expect events. Without the setup, any code using events will just crash with NPE. With the setup, Mockito should complain when no events related code was executed to keep the tests clean (to avoid devs doing copy&paste of code they don't understand). Re annotations: I don't think that the annotations are a good solution here. They are nice for code which needs to be migrated from old Mockito but for new code, they are too coarse for all my use cases. On the other hand, I don't like the idea of having to add |
Thank you for reply! I'm still interested in your use case hence my further questions.
Why? Hand stub implementation should have all the common reusable code. In the test, you can just call "mock.setupEventPublishing()" or something like that.
Provided that we add both: stubbing + mock level strictness setting, then you have 2 options:
Thoughts? |
@mockitoguy Thanks. I feel that I don't have a completely clear picture, yet.
So a base class with a lot of empty methods and then using spy() to overwrite them? That might work but I don't like spy() (had too many problems with that approach in the past). It still feels like more effort than simply wire some map methods to API methods using
Option #1 is too coarse for me. With option #2, I would have to add 7 lines of code to the block in #792 (comment) and you were complaining that the code was already too complicated :-) That's why I suggested to have an "enable some options for a code" block approach. In my case, I could then wrap the call to the |
Hand stub/mock is a class that is completely tailored for the business of your tests. Sometimes referred as "test fixture code", "test utils", "test support classes". Hand mocks don't use Mockito, not even spy(). Hand stub needs to be coded and maintained but it provides superior readability and usefulness because it is tailored to your domain.
We could potentially provide a method like: Mockito.lenient(() -> { ... }); Not sure about this, though. It's a very different API than what we have so far. Per-mock + per-stubbing leniency feels like a happy medium: if repeated "lenient()" is a problem, you can configure it per the entire mock. Per-mock is coarse grained but on the other hand it is conventional and simple. Big "lenient" block might lead to weird test code, developers putting more code inside the block, the intent of the code getting obscured. |
Updated the description with the problem statement and proposed API changes. Feedback before we start coding? |
Update: work in progress in PR #1272. Friendly ping for review of the design outlined in this ticket description! |
I'm pondering the two API designs: Why not What about Also, this is shorter. I feel |
Yeah, possibly. We also want to have those new API methods consistent / similar with the existing API:
How would this play with doReturn syntax?
The former is not possible because Thank you for thoughtful feedback! |
How about a fluent API to mock several methods of a mock at once? That way, I could specify the lenient once at the top? |
Can you write a comment with an example how it would look? This would help us make a decision. Sorry for late answer. Xmas break :) I made progress on the ticket, though! |
I'm finalizing the implementation in #1272, it will be released within days. |
Problems
Overview of strictness: #769
Suggested solution
New public API overview:
Details:
Examples
Original report
The new UnnecessaryStubbingException logic is great. However, it is sometimes useful to disable this detection for specific methods. Could it be possible to add an annotation that says "unnecessary stubs should not be detected in this method"?
This would also make it easier to migrate from Mockito 1.* to Mockito 2. This is the case in the project I am currently working at, where we have created some utility methods that creates a mock and configures it to fit most of our use cases. As we use this method at many places, where different configuration is needed, it will cause a lot of unnecessary stubbing. Thus, we would like to keep this method out of unneccessary stubbing-check, while doing this check on the rest of the code base.
The text was updated successfully, but these errors were encountered: