-
Notifications
You must be signed in to change notification settings - Fork 40.8k
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
@MockBean fields are not reset for JUnit 5 @Nested tests #12470
Comments
As a side note: I think this might also be the reason why Spring REST Docs did not work with |
Thanks for the minimal sample, @slu-it. The problem is that I'm rather surprised that there's a second application context created for @sbrannen can you help us to get to the bottom of this one please? |
A bit of hunting around led me to NestedTestsWithSpringAndJUnitJupiterTestCase.java . It's clear from those tests that the behaviour I described above is intentional and that my hunch was wrong. It's also taught me that in this case The change described above makes the test code look like this: package example;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@SpringBootTest
@ExtendWith(SpringExtension.class)
class FailingTests {
@Autowired
ServiceA cut;
@MockBean
ServiceB serviceB;
@Nested
@SpringBootTest
class NestedTest {
@MockBean
ServiceB serviceB;
@Test
void mockWasInvokedOnce() {
cut.foo();
verify(serviceB, times(1)).bar();
}
@Test
void mockWasInvokedTwice() {
cut.foo();
cut.foo();
verify(serviceB, times(2)).bar();
}
}
} I wonder if we can do something when calculating the context cache key for |
In the meantime I checked whether or not the Spring REST Docs issue still exists. Tested it with Spring Boot |
Here's a possible fix. Can you take a look please, @philwebb? With the fix in place, these failing tests are now actually working tests: package example;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@SpringBootTest
@ExtendWith(SpringExtension.class)
class FailingTests {
@Autowired
ServiceA cut;
@MockBean
ServiceB serviceB;
@Nested
@SpringBootTest
class NestedTest {
@Test
void mockWasInvokedOnce() {
cut.foo();
verify(serviceB, times(1)).bar();
}
@Test
void mockWasInvokedTwice() {
cut.foo();
cut.foo();
verify(serviceB, times(2)).bar();
}
}
} Adding a unit test for this is a challenge. As soon as I add an optional dependency on the JUnit Jupiter API to spring-boot-test, Eclipse defaults to using the JUnit 5 runner for all the tests in that project and you have to manually tell it to use JUnit 4 instead. I think that's too high a price to pay. We could add some use of |
@wilkinsona is the second |
Yes. Without it, you get a second application context created for |
Ok, makes sense. |
I'm actually very surprised by that. I thought that a In fact, I'm nearly 100% certain that an So, perhaps it's just the magic of Spring Boot Test that is automagically finding the configuration for the test But I'd have to debug that to see what's really going on.
Many people actually assume that would be the case, since the TCF supports configuration inheritance within a test class hierarchy. However, Spring does not yet have any support for "inheriting" configuration from an enclosing class. See SPR-15366 for details. Unfortunately, resolving SPR-15366 is not so easy (if even feasible) since the entire annotation processing infrastructure within the core Spring Framework (and Spring portfolio projects for that matter) has zero support for searching for annotations on an enclosing class. 😞
The "right way" for the time being is simply to redeclare all of the annotations declared on the enclosing class. A custom composed annotation can alleviate the pain here, but in general I admit that it is quite a nuisance. |
Hmmm... what is the second context created from? For example, what |
That of course sounds nice for now, but I'm wondering what the implications would be if SPR-15366 is resolved at a later date. Maybe the result of SPR-15366 would be an opt-in feature for turning on "inheritance from enclosing class" in order to remain backwards compatible. Will have to ponder it... 😉 |
If you have personally confirmed that behavior with Boot, I would appreciate if you could add a corresponding comment to SPR-15366. TIA |
I'm not sure as I didn't dig that far. I can do though.
I have. I'll do a bit more digging so I can provide some more details and comment on SPR-15366. |
Thanks, @wilkinsona! 👏 |
No need to do that: your input in SPR-15366 is sufficient. |
We're going to wait for the SPR-15366 fix before looking at this. |
unit test stay in junit4
Hello, I'm having similar issue with BDDMockito.willThrow
|
Same problem here, add |
Adding springbootcontext in nested classes doesnt work for me. I had to reset mock after each test |
Please note that spring-projects/spring-framework#19930 has been resolved in time for Spring Framework 5.3 RC2. See the "Deliverables" section of that issue (and the following commits) for details on what's included in RC2. |
I've tried a slightly updated version of the original sample with Spring Boot 2.4.0-M4 which uses Framework 5.3.0-RC2. The problem still occurs, however I think that's to be expected as we're not making use of any of the changes made in Framework to find test configuration in an enclosing class. This results in a cache miss as the merged context configurations for FailingTests and FailingTests.NestedTest differ. I think we need to make some changes to use |
Spring Framework 5.3 is now GA, and the @wilkinsona, let me know if you run into any issues getting everything working with the new nested configuration support. |
I just tried this with Spring Boot 2.4.0-RC1, but the There are 2 tests there: If you want me to create a separate issue, I can also do that. |
Thanks for trying out the RC, @wimdeblauwe. Please do open a new issue. It's not immediately apparent to me what the difference is between this test class that was part of the commit that closed this issue and your example. It may be the use of |
Issue #23984 created. |
Hello Spring Boot team!
Today a colleague of mine came across strange behaviour when writing some JUnit 5 tests. It seems that
@MochBean
annotated fields, or more specifically the actual mocks behind that, are not reset for@Test
methods residing in@Nested
inner classes.Example:
One of these tests will fail because the mock will have been invoked 3 times at that point.
Tested with Spring Boot
2.0.1.BUILD-SNAPSHOT
and JUnit5.1.0
on2018-03-13
at around8:00 PM
. (also tested with2.0.0.RELEASE
earlier)I created a simple example application (thx start.spring.io ;) ) to reproduce the issue: https://github.com/slu-it/bug-spring-mockbean-reset
I also tried to find the cause of the issue, but I might have underestimated the complexity of the
spring-test
andspring-boot-test
libraries... As far as I can see, the application context returned from theTestContext
instance for tests within the@Nested
class does not include references to theServiceB
bean. (From debuggingMockitoTestExecutionListener
)When I debug the same location for a test without the
@Nested
part, the returned context contains a clearly identifiable instance ofServiceB
.I think the correct behaviour would be to have a parent child relationships between application contexts in case of inner classes (like
@Nested
JUnit 5 test classes).The text was updated successfully, but these errors were encountered: