-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Introduce MockitoExtension for JUnit Jupiter (a.k.a. JUnit 5) #445
Comments
I think this issue can be merged with #390. Our plan there was to introduce JUnit 5 compatibility in Mockito 3.0 (since 2.0 is on the verge on being released in a couple of weeks). |
Sorry: I overlooked #390. Feel free to merge it as you see fit. thanks! |
Let's track the JUnitExtension in this issue and list the specifications of the |
@sbrannen Any news on this ? |
@bric3, the So, it's really up to the Mockito Team to decide when they want to take it over. |
@sbrannen this one -> example/mockito/MockitoExtension ? |
Yep, that's the one. |
Thanks @sbrannen. |
Well... that's the million dollar question -- isn't it? 😉 All kidding aside, we are currently working on JUnit Jupiter 5.0 M3, but we still have quite a way to go before a GA release. So, although I cannot promise that the |
OK that's good to know. Anyway we have to release 2.1 before starting 3.0 anyway. And a some API design work on 2.1 too, that may delay JUnit5 integration in the 3.0 beta phase. |
Shall we start with fleshing out the specifications of the extension? Would like to have a working prototype when JUnit 5 is released, ETA Q1 2017. |
Yes go for it, I'll create a submodule junit 5 at this time since JUnit 5 is a near complete rewrite, with a lot of binary incompatibilities. |
+1 Do we want to completely decouple mockito-core module from JUnit? |
@szczepiq I am not sure, but supporting both @sbrannen Are you aware of other framework users depending on JUnit which employ this approach or is there a different and better solution? |
In the That's all within a single JAR, and there are no issues since each of those is an optional dependency (in terms of the Maven POM). Thus, projects that consume As long as it's clear what developers need to consume for a given testing framework, it shouldn't be a problem having a single artifact. For example, with Spring, JUnit 4 users use the |
If you do opt for separate modules (i.e., Maven artifacts), I would recommend against The problem with naming such an artifact FYI: I have just changed the title of this issue to reflect this fact. |
Okay that seems reasonable, so let's take the package approach! My experience with jar configurations is limited, so in terms of the configuration of users' |
@sbrannen thank you for describing and suggesting an approach. I like the idea of separation at the Java package level in the same jar. We can go for separate jars only when we have to (e.g. when JUnit versions clash). Do you have integration tests that demonstrate correct behavior with different test frameworks / different versions of test frameworks? |
Is that a rhetorical question? 😉 Yes, of course we have tests for the Spring Framework. I'd be ashamed to let Regarding the package structure, if you look here you'll see the following:
So that's how we split up the functionality within the We naturally have unit and integration tests for our JUnit 4, JUnit Jupiter, and TestNG support which you can find in various subpackages here. Within the IDE, we execute TestNG tests with the TestNG plugin (e.g., for Eclipse); we execute JUnit 4 tests with the IDE's built-in support; and we execute JUnit Jupiter tests with the Within the Gradle build, we execute TestNG, JUnit 4, and JUnit Jupiter tests via Gradle's standard I'm not sure if I've now said too much or too little. So if you still have questions, just ask! Regards, Sam |
This week I had an interesting discussion with 2 of my student colleagues who are currently analyzing and documenting the architecture of JUnit. In this discussion we talked about the architecture in general as well as the extensions and in particular the MockitoExtension. In this discussion I expressed my concerns regarding the new approach with injecting Mocks based on parameters. My colleagues told me that JUnit5 focuses more on testing with interfaces, in which the tests are also interfaces. Since interfaces do not have fields, field injection of mocks is not possible. Therefore the JUnit team proposes/chose to inject mocks via parameters. Maintaining mocks is one of the most precarious tasks when working on a test suite and we at Mockito regularly receive feedback where Mocks are misbehaving. This is the exact reason that we are shipping features such as As a result, our general goal for Mockito 3 is to leverage the available tools even more to prevent developers from making mistakes. Most notably I am personally advocating for increasing type-safety of our various features such that developers can rely on the compiler to warn them. Since running tests is a costly task and sometimes takes over an hour before CI finished all tests, the earlier developers can receive feedback regarding misconfiguration, the better. Having said that, let's go back to the parameter injection currently available with JUnit5. While the status quo is field injection, a compiler can warn developers when they have a typo in the usage of a Mock. E.g. when a user wants to use While we are striving to prevent developers of making mistakes by leveraging the compiler, adopting parameter injection feels like a step back to me. For this reason I am not in favor of going for parameter injection, but given the need for writing tests as interfaces I am not sure what our options are. Of course we have not started writing an extension, so I have not tried this out. But I do think we should resolve this issue before coming up with a good story for Mockito 3 + JUnit5. CC @LiamClark @Tarovk |
How is parameter injection different from field injection? Edit: To rephrase my question a bit: How is field injection safer than parameter injection? Is there some Mockito feature I'm not aware of? |
@marcphilipp I think he was talking about this error case: public class Test {
@Mock Dependency mock1;
@Before public void setUp() {
when(mock1.foo()).thenBar();
}
@Test public void test() {
// mock2 is a compile error
new SUT(mock2).target();
}
} class Test {
@BeforeEach void setUp(@Mock Dependency mock1) {
when(mock1.foo()).thenBar();
}
@Test void test(@Mock Dependency mock2) { // or @Mock(name="mock2")
// mock2 is a newly created a mock without .foo() stubbing
new SUT(mock2).target();
}
} The extension can't know if you intentionally want a new mock in your test method or you made a typo, while javac knows that you're referencing a non-existent field. Re edit: The current extension impl caches the mocks: https://github.com/junit-team/junit5-samples/blob/026a9d9abe06b6173398c1a2518793259cd190f2/junit5-mockito-extension/src/main/java/com/example/mockito/MockitoExtension.java#L57 |
Hm, looking at it again, it seems that the breaking change is actually in an internal API, namely |
String testName = event.getTestClassInstance().getClass().getSimpleName()
+ "." + event.getTestMethodName(); Currently, Alternatively, Moreover, I think Thoughts? |
@marcphilipp |
it would be nice if the MockitoExtension works together with @sbrannen you guys already thought about how to integrate that? |
@paulmiddelkoop There's no lifecycle support for dynamic tests and we currently have no plans to add any, i.e. there's no hook to reset mocks after a dynamic test. You could use |
I agree with @marcphilipp: due to the lack of lifecycle support for dynamic tests (see the Dynamic Test Lifecycle note in the User Guide), you'll have to reset mocks manually. Note, however, that you'll likely want to reset them within each |
Thanks @marcphilipp and @sbrannen for the explanation. Any reason why there is no lifecycle support for dynamic tests? For now I ended up with a utility function for this: fun dynamicMockitoTest(testInstance: Any, displayName: String, executable: () -> Unit): DynamicTest {
val mockito = Mockito.mockitoSession()
.initMocks(testInstance)
.strictness(Strictness.STRICT_STUBS)
.startMocking()
try {
return dynamicTest(displayName, executable)
} finally {
mockito.finishMocking()
}
} However this will not work if I also have regular I think a simpler integration is needed for a good adoption of dynamic tests. It's a regular use case to use mocking inside dynamic tests, right? |
Dynamic tests are intended for simple use cases that do not need lifecycle callback support. If one needs lifecycle callback support, it is then recommended to use parameterized tests.
Actually, that won't work at all, not even for dynamic tests. Your current code example initializes and resets mocks around the construction of the You would instead need to create your own wrapper around the invocation of your
We don't know. The dynamic test support is an experimental feature. So that remains to be seen. 😉 |
@paulmiddelkoop if you create an extension method you don't need to pass in fun Any.dynamicMockitoTest(displayName: String, executable: () -> Unit): DynamicTest =
dynamicTest(displayName) {
val mockito = Mockito.mockitoSession()
.initMocks(this@dynamicMockitoTest)
.strictness(Strictness.STRICT_STUBS)
.startMocking()
try {
executable()
} finally {
mockito.finishMocking()
}
} and the usages of
Well, now you have two votes for mocking in dynamic tests. |
@paulmiddelkoop & @TWiStErRob, if you want support for lifecycle callbacks for dynamic tests in JUnit Jupiter, the best place to make your wishes heard is junit-team/junit5#378. |
What is the ETA for Mockito 3.x? I'd love to see this feature. |
The intent now is to ship junit5 support in a separate artifact with mockito 2 |
@TimvdLippe That is great news - maybe the milestone should be updated to reflect this? |
A little house-keeping: The change in #438 was made to allow Mocks to be generated for the parameters to test and support methods. |
@smoyer64 That is correct. I wanted to publish the extension as it was lingering for too long and thus did not want to wait on discussion about "new features" (compared to the 4 runner). Please open a new issue to discuss that change. |
I haven't had a chance to try out the official The I have to admit I was pretty excited to see this released yesterday since it would provide an official way to use Mockito with JUnit 5. Unfortunately we're already using the |
@smoyer64 as I said I am open for adding that feature, but I would like to discuss it first in a separate issue. So please open one with your use-case so we can discuss 😄 |
@TimvdLippe Sorry I wasn't clear in my last comment. I do realize that this issue is closed and went off to write the feature request you asked for (and got side-tracked by an emergency with one of the non-profits I help run). I also looked at all the JUnit 5 related issues that were already posted. Would it be worth having a JUnit 5 label to help organize them? In any case, I've now added #1348 ... please let me know if additional information is needed. Thanks for all the hard work you've done to provide official JUnit 5 support in Mockito! |
As there is not yet an official artifact for Mockito 2.x released that adds a MockitoExtension (see [^1] and [^2]), use a custom one from [^3] for now. This is urgently needed to enable unit testing of controllers WITHOUT the need to write a @SpringBootTest which would involve starting a WebContext for the tests. (That would be more in the direction of a integration test than necessary... See [^4]) Needs to be replaced with the official extension once released. [^1]: mockito/mockito#445 [^2]: mockito/mockito#1221 [^3]: https://github.com/JeffreyFalgout/junit5-extensions/tree/master/mockito-extension [^4]: https://thepracticaldeveloper.com/2017/07/31/guide-spring-boot-controller-tests
Since this is one of the top Google results for trying to get this working, and I'll likely Google it again, I'd like to add that this is in the mockito-junit-jupiter project. Maven Central link mockito-junit-jupiter |
New Features in JUnit 5
The JUnit Jupiter extension model in JUnit 5 introduces support for constructor and method parameter resolution (i.e., dependency injection).
Specifically, third parties can implement the
ParameterResolver
extension API to inject dependencies, mocks, etc. into constructors and methods. In addition, theTestInstancePostProcessor
extension API can be implemented to post-process a test instance (e.g., to perform field injection).Status Quo
Mockito supports field injection for mocks via the
@Mock
annotation. In addition, #438 allows@Mock
to be declared on parameters for constructors and methods which makes@Mock
support an ideal candidate for both theTestInstancePostProcessor
andParameterResolver
extension APIs in JUnit Jupiter. In fact, the JUnit Team has already developed a proof of concept: see theMockitoExtension
in the junit5-mockito-extension sample project.Deliverables
MockitoExtension
for JUnit Jupiter to replace the proof of concept from the JUnit team.The text was updated successfully, but these errors were encountered: