https://github.com/antoinemeyer/mock-in-bean
Surgically Inject Mockito Mock/Spy in Spring Beans
https://github.com/antoinemeyer/mock-in-bean
junit mockito spring-boot
Last synced: 5 months ago
JSON representation
Surgically Inject Mockito Mock/Spy in Spring Beans
- Host: GitHub
- URL: https://github.com/antoinemeyer/mock-in-bean
- Owner: antoinemeyer
- License: apache-2.0
- Created: 2021-03-16T22:42:25.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2025-01-09T21:44:31.000Z (over 1 year ago)
- Last Synced: 2025-07-30T03:41:13.932Z (10 months ago)
- Topics: junit, mockito, spring-boot
- Language: Java
- Homepage:
- Size: 109 KB
- Stars: 95
- Watchers: 2
- Forks: 12
- Open Issues: 3
-
Metadata Files:
- Readme: README.MD
- License: LICENSE.txt
Awesome Lists containing this project
README
# Mock in Bean
[@MockInBean](src/main/java/com/teketik/test/mockinbean/MockInBean.java) and [@SpyInBean](src/main/java/com/teketik/test/mockinbean/SpyInBean.java) are alternatives to @MockBean and @SpyBean for Spring Boot tests *(>= 2.6.15 including >= 3.X.X)*.
They surgically replace a field value in a Spring Bean by a Mock/Spy for the duration of a test and set back the original value afterwards, leaving the Spring Context clean.
**@MockInBean 'mocks a bean in a bean'** whereas **@MockBean 'mocks a bean in the whole context'**.
# But why ?
## The problem:
Spring Context pollution was a fairly common problem before the introduction of @MockBean and @SpyBean. Many developers would inject mocks in beans during tests through `@InjectMock` or using manual setters and often forget to set back the original field values which would leave the Spring context polluted and cause test failures in unrelated tests.
@MockBean and @SpyBean solved this issue by providing Mockito injection directly in the Spring Context but introduced an undesirable side-effect: their usage dirties the context and may lead to the re-creation of new Spring contexts for any unique combination, which can be incredibly time-consuming. See [The Problems with @MockBean](https://www.baeldung.com/spring-tests#2-the-problems-withmockbean)
### The solution:
Assuming you really need to run the test in the Spring Context, the most straight-forward solution is still to inject your mock/spy in your bean and reset it afterwards.
**@MockInBean and @SpyInBean brings the convenience of @MockBean and @SpyBean and do just that:**
1. Set the mock/spy in the bean.
2. Replace the mock/spy by the original value afterwards.
# Example:
Assuming that you want to test the following service:
```java
@Service
public class MyService {
@Autowired
protected ThirdPartyApiService thirdPartyService;
@Autowired
protected ExpensiveProcessor expensiveProcessor;
public void doSomething() {
final Object somethingExpensive = expensiveProcessor.returnSomethingExpensive();
thirdPartyService.doSomethingOnThirdPartyApi(somethingExpensive);
}
}
```
You can write your test this way:
```java
@SpringBootTest
public class MyServiceTest {
@MockInBean(MyService.class)
private ThirdPartyApiService thirdPartyApiService;
@SpyInBean(MyService.class)
private ExpensiveProcessor expensiveProcessor;
@Autowired
private MyService myService;
@Test
public void test() {
final Object somethingExpensive = new Object();
Mockito.when(expensiveProcessor.returnSomethingExpensive()).thenReturn(somethingExpensive);
myService.doSomething();
Mockito.verify(thirdPartyApiService).doSomethingOnThirdPartyApi(somethingExpensive);
}
}
```
**What happens**:
Before each test:
- `MyServiceTest.thirdPartyService` will be created as a mock and injected in the target of @MockInBean: `MyService`.
- `MyServiceTest.expensiveProcessor` will be created as a spy of the bean `expensiveProcessor` and injected in the target of @SpyInBean: `MyService`.
After the tests of `MyServiceTest` are done:
- `MyService.thirdPartyService` will be reset to the original Spring bean `thirdPartyService`
- `MyService.expensiveProcessor` will be reset to the original Spring bean `expensiveProcessor`
# Usage:
Simply include the maven dependency (from central maven) to start using @MockInBean and @SpyInBean in your tests.
```
com.teketik
mock-in-bean
boot2-v1.9
test
```
@MockInBean and @SpyInBean also support:
- Injection in multiple Spring beans: *Repeat the annotation on your field*.
- Injection in bean identified by name if multiple instances exist in the context: *Specify a `name` in your annotation*.
Checkout the javadoc for more information.
# Limitations:
This approach has some limitations compared to the @MockBean/@SpyBean equivalent:
- There is currently no isolation per thread. It is not advised to use this library in parallel test suites. (Parallel test execution is supported experimentally in https://github.com/antoinemeyer/mock-in-bean/tree/v2.1-RC).
- Operations on beans happening within another bean's constructor will not be performed against the mocks since the mocks are injected directly into the fields. Do not use @MockInBean/@SpyInBean for beans that are manipulated in constructors.