question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Context is reloading when using @MockBean

See original GitHub issue

I’m having trouble running my tests, when using the @MockBean annotation to mock a behavior, Spring reloads the context. As the example below:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = WalletPaymentApplication.class, properties = { "spring.cloud.config.enabled:true",
		"management.security.enabled=false" })
@RabbitListenerTest
@Transactional
@ActiveProfiles("test")
public abstract class WalletPaymentApplicationTests {

	public static final String JSON_MEDIA_TYPE = "application/json;charset=UTF-8";

	@Autowired
	protected EntityManager em;
	
	@MockBean
        protected RestTemplate template;

	protected MockMvc mockMvc;

	@Autowired
	protected WebApplicationContext wac;

	@SpyBean
	protected DomainEventPublisher domainEventPublisher;
}
public class PaymentControllerTest extends WalletPaymentApplicationTests {
 
	@MockBean
	private PaymentService service;

	@Before
	public void setUp() {
		mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
	}
	
	@Test
	public void newPayment() throws Exception {

		when(service.newPayment(any(PaymentCommand.class)))
				.thenReturn(CompletableFuture.supplyAsync(() -> new PaymentResumeData(any(MerchantData.class),
						any(CashbackData.class), any(BigDecimal.class), anyListOf(SummaryCardData.class))));

		String payload = "{\"paymentId\": \"4548888888888888\","
				+ "\"customerId\": \"100\","
				+ "\"merchantSubKey\": \"4df4447hjh8g-4d4g5f-vdgfg\"," 
				+ "\"amount\": 650.00,"
				+ "\"taxId\": \"frfr6-d4g4v7-4b8f\"}";

		MockHttpServletRequestBuilder builder = post("/payment").contentType(MediaType.parseMediaType(JSON_MEDIA_TYPE))
				.content(payload);

		mockMvc.perform(builder).andExpect(request().asyncStarted()).andExpect(status().isOk());
}
public class PaymentServiceTest extends WalletPaymentApplicationTests {

    @Test
    public void newPayment() throws Exception {
        String date = formatarData(new Date(), "yyyy-MM-dd HH:mm:ss");
        when(template.getForObject(configureUrl(customerUrl), CustomerRows.class, "100"))
                .thenReturn(fixture.fixtureCustomer());
        when(template.getForObject(merchantUrl, MerchantRows.class, "wevf-f5vgb-5f", date))
                .thenReturn(fixture.fixtureMerchant());
        when(template.getForObject(configureUrl(cardUrl), CardRows.class, "100"))
                .thenReturn(fixture.fixtureCard());

        PaymentResumeData resume = service.newPayment(new PaymentCommand("12378000000000000", "100",
                "wevf-f5vgb-5f", 3000L, "85640827807")).get();

        Payment payment = repository.getPaymentById(new PaymentId("12378000000000000"))
                .orElseThrow(() -> new PaymentNotFoundException());

        assertEquals(BigDecimal.valueOf(150.60), resume.getBalance());
        assertEquals("promocode", resume.getCashback().getType());
}

For test scenario, the spring recharges the context 3 times. If i remove the annotation, spring starts the context only once

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:1
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

11reactions
SeriyBgcommented, May 29, 2019

@eutiagocosta @philwebb I had a similar issue, and I have overcome it by doing the following. I’m having in the test sources a separate configuration for bean mocking:

@Configration
public class MockConfig {

  @Bean
  public ExternalService externalService() {
    return mock(ExternalService.class);
  }
}

And in the test class I’m just using @Autowire for the ExternalService:

@Autowire
private ExternalService externalService;

This is the workaround, but it does the job for me.

0reactions
prp28commented, Jan 19, 2021

@surapuramakhil Yes, for only particular test class, you can create mock object and inject it on demand by using ReflectionTestutils in test class.

ExternalService externalService = Mockito.mock(ExternalService.class);

@Test
void testMockBeanWorksWithInjection (){
ReflectionTestUtils.setField(<AutowiredClassWhichHasMockField>, "externalService", externalService);
// Rest of test code
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Using @MockBean in tests forces reloading of Application ...
As you said, if you move the mock bean to the parent class the context doesn't get reloaded, which makes sense as the...
Read more >
Avoid Reloading Application Context on Unit Tests - DZone
The @MockBean annotation will add mock objects into the Spring application context. As a result, the mocked item will replace any existing bean ......
Read more >
Improve Build Times with Context Caching from Spring Test
Understand how the Context Caching mechanism from Spring Test can improve your build time while resuing an already started context.
Read more >
Micronaut Test
1, Like the previous example a Mock is defined using @MockBean ... Using @Property. package io.micronaut.test.spock import io.micronaut.context.annotation.
Read more >
Optimizing Spring Integration Tests - Baeldung
Every time @MockBean appears in a class, the ApplicationContext cache gets marked as dirty, hence the runner will clean the cache after the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found