Springboot @MockBean not being injected when running other tests with whole context
Asked Answered
G

1

8

I'm facing some problems when trying to inject a bean with the annotation @MockBean inside a Junit test. As a result I get the real service injected instead of the mocked one, but the weird behavior is that this only happens when running tests with maven verify (together with other integration tests).

Basically, the bean I want to mock is injected inside a Listener (@Component) that is triggered by a message sent on the queue during the integration test. When the listener runs, the service inside it is the real one instead of the mocked one.

It seems to me that, when running other tests, the real bean is previously injected inside the context and @MockBean, although it should restart the spring context, does not replace the existing bean with the mocked one when it encounters a bean of the same type.

This is really a strange behavior, because documentation says "Any existing single bean of the same type defined in the context will be replaced by the mock". Well, this is not happening.

Below you find snippets showing how this is done.

Service to be mocked is:

@Slf4j
@Service
@Transactional
public class SomeServiceImpl implements SomeService {
   
   @Override
   @Async
   public void doStuff(){
      ...
   }
}

A listener that injects my service like this

@Slf4j
@Component
@Transactional
public class SagaListener {
    
    @Autowired
    private SomeService someService;
    
    @JmsListener(destination = "${mydestinationtopic}", containerFactory = "myFactory",
    subscription = "my-subscription", selector = "eventType = 
    'MY_EVENT'" )
    public void receive(MyEventClass event) {
        someService.doStuff();
    }
}

And here is my test class

@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class SagaListenerIT {

    @MockBean
    private SomeService someService;
    
    @Autowired
    private Sender sender;

    @Test
    public void createNamespaceSuccess() throws InterruptedException {
        ...
        sender.send(event, event.getEventType(), myTopic);
        BDDMockito.then(someService).should().doStuff();
    }

}

As a result I get that mockito says that someService made 0 invokations, and this is because the real service is being called.

Why isn't @MockBean replacing the real bean? Should't the context be reinitialized?

I've tried to add @DirtiesContext annotation in other tests and in that case everything works, but this is not a clean solution.

here is a portion of my pom where failsafe plugin is defined. It's a really simple one by the way:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Thank you

Galiot answered 8/9, 2020 at 14:1 Comment(4)
Hi & Welcome! Fine but difficult question(s)! Your conclusion, that the real bean has not been replaced, could be wrong, it could also just mean that the mock method was not invoked yet ..see nice article on "JMS Integration Testing", 2017, related questionBrownlee
Hi, thank you for your answer. I omitted a Thread.sleep(1000); in my example, but actually it is there. The point is that the listener actually runs before verifying it by mockito. I can see it during debug and in the logs I put in the middle. What is strange is, If I run it alone, it works, and if I run all tests using intellij it still works. The failure only comes when running with maven verifyGaliot
Strange! :) Is it possible that "failsafe:verify" activates some (spring)profiles/changes configuration/uses other context/maybe even other environment!? (please also share the failsafe portion of your pom.) The only use of DirtiesContext(, I see,) is to restore mocked beans(="clean dirty context")...so the contrary of your issue (and not on the annotated test, but preceding or (by default) the following ones) - please clarify this. And else: the significant differences between maven and IDE testing are in "class loading" and probably "test execution order"..Brownlee
I've added my failsafe configuration. Actually you're right, the difference between my IDE and maven verify is the execution order, and in the first case this test is executed first! That's why it works. Concerning DirtiesContext if I put it on the other test class (there are only two integration test classes) leaving the default behavior, everything works, but if I put it on this class using classMode = ClassMode.BEFORE_CLASS, this test fails saying that the mock was not invoked.Galiot
I
-1

I had this issue. In my case, I added

@ContextConfiguration(classes = {SagaListener.class}) // Inject the MockBean in this class

on SagaListenerIT

Isla answered 13/6 at 16:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.