Home > front end >  @Mock results in exception but @MockBean works in spring boot application
@Mock results in exception but @MockBean works in spring boot application

Time:01-20

I'm learning to write test cases for controller layer in a spring boot application. I've used @Mock to mock the repository interface in service layer test case and it worked/ compiled & test case got executed. In controller layer test case @Mock is resulting in exception but @MockBean works.

I tried to understand the difference between @Mock & @MockBean from other resources, but expecting more clarity why @Mock works in service tests but does not work in controller tests.

Please refer my code -

  1. Entity

    package com.learning.microservices.userservice.model;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import org.springframework.data.annotation.Id;
    import org.springframework.data.mongodb.core.mapping.Document;
    
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Document(collection = "Consumer")
    public class Consumer {
        @Id
        private Long consumerId;
        private String consumerName;
        private String emailAddress;
    }
    
  2. Repository

    package com.learning.microservices.userservice.repository;
    
    import com.learning.microservices.userservice.model.Consumer;
    import org.springframework.data.mongodb.repository.MongoRepository;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public interface ConsumerRepository extends MongoRepository<Consumer, Long> {
    }
    
  3. Service

    1. Interface

      package com.learning.microservices.userservice.Service;
      
      import com.learning.microservices.userservice.model.Consumer;
      
      public interface ConsumerService {
          public Consumer saveConsumer(Consumer consumer);
      }
      
    2. Implementation

      package com.learning.microservices.userservice.Service;
      
      import com.learning.microservices.userservice.model.Consumer;
      import com.learning.microservices.userservice.repository.ConsumerRepository;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      @Service
      public class ConsumerServiceImpl implements ConsumerService {
          @Autowired
          ConsumerRepository consumerRepository;
      
          public Consumer saveConsumer(Consumer consumer){
              if (consumerRepository.findById(consumer.getConsumerId()).isPresent())
                  throw new RuntimeException("Consumer already exists");//should return 409
              return consumerRepository.save(consumer);
          }
      }
      
  4. Controller

    package com.learning.microservices.userservice.controller;
    
    import com.learning.microservices.userservice.Service.ConsumerService;
    import com.learning.microservices.userservice.model.Consumer;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
    
    import java.net.URI;
    
    @RestController
    @RequestMapping("/consumers")
    public class ConsumerController {
    
        @Autowired
        ConsumerService consumerService;
    
        @PostMapping
        public ResponseEntity<Consumer> saveConsumer(@RequestBody Consumer consumer) {
            Consumer savedConsumer = consumerService.saveConsumer(consumer);
            URI consumerLocation = ServletUriComponentsBuilder.fromCurrentRequest().path("/{consumerId}").buildAndExpand(savedConsumer.getConsumerId()).toUri();
            return ResponseEntity.created(consumerLocation).build();
        }
    
    }
    
  5. Test Cases

    1. Service

      package com.learning.microservices.userservice.Service;
      
      import com.learning.microservices.userservice.model.Consumer;
      import com.learning.microservices.userservice.repository.ConsumerRepository;
      import org.junit.jupiter.api.AfterEach;
      import org.junit.jupiter.api.BeforeEach;
      import org.junit.jupiter.api.Test;
      import org.junit.jupiter.api.extension.ExtendWith;
      import org.mockito.InjectMocks;
      import org.mockito.Mock;
      import org.mockito.junit.jupiter.MockitoExtension;
      
      import java.util.Optional;
      
      import static org.assertj.core.api.Assertions.assertThat;
      import static org.mockito.BDDMockito.given;
      
      @ExtendWith(MockitoExtension.class)
      class ConsumerServiceImplTest {
      
          @Mock
          private ConsumerRepository consumerRepository;
      
          @InjectMocks
          private ConsumerServiceImpl consumerService;
      
          private Consumer consumer;
      
          @BeforeEach
          public void setUp() {
              consumer = Consumer.builder()
                      .consumerId(1L)
                      .consumerName("Test Consumer")
                      .emailAddress("[email protected]")
                      .build();
          }
      
          @AfterEach
          public void tearDown() {
              consumer = null;
          }
          @Test
          void saveConsumer() {
              given(consumerRepository.findById(consumer.getConsumerId())).willReturn(Optional.empty());
              given(consumerService.saveConsumer(consumer)).willReturn(consumer);
              Consumer savedConsumer = consumerService.saveConsumer(consumer);
              assertThat(savedConsumer).isNotNull();
          }
      
      }
      
    2. Controller

      package com.learning.microservices.userservice.controller;
      
      import com.fasterxml.jackson.databind.ObjectMapper;
      import com.learning.microservices.userservice.Service.ConsumerServiceImpl;
      import com.learning.microservices.userservice.model.Consumer;
      import org.junit.jupiter.api.AfterEach;
      import org.junit.jupiter.api.BeforeEach;
      import org.junit.jupiter.api.Test;
      import org.mockito.InjectMocks;
      import org.mockito.Mock;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
      import org.springframework.http.MediaType;
      import org.springframework.test.web.servlet.MockMvc;
      import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
      import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
      
      import java.nio.charset.StandardCharsets;
      
      import static org.mockito.BDDMockito.given;
      
      @WebMvcTest
      class ConsumerControllerTest {
      
          private ObjectMapper objectMapper;
          private Consumer consumer;
      
          @Autowired
          private MockMvc mockMvc;
      
          @Mock
          private ConsumerServiceImpl consumerService;
      
          @InjectMocks
          private ConsumerController consumerController;
      
      
          @BeforeEach
          public void setUp() {
              consumer = Consumer.builder()
                      .consumerId(1L)
                      .consumerName("Test Consumer")
                      .emailAddress("[email protected]")
                      .build();
              objectMapper = new ObjectMapper();
          }
      
          @AfterEach
          public void tearDown() {
              consumer = null;
              objectMapper = null;
          }
      
          @Test
          void saveConsumer() throws Exception {
              given(consumerService.saveConsumer(consumer)).willReturn(consumer);
              mockMvc.perform(MockMvcRequestBuilders.post("/consumers")
                      .contentType(MediaType.APPLICATION_JSON)
                      .content(objectMapper.writeValueAsString(consumer))
                      .characterEncoding(StandardCharsets.UTF_8)
              )
              .andExpect(MockMvcResultMatchers.status().isCreated());
          }
      }
      
    3. Exception logs

      java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@5f8a02cf testClass = com.learning.microservices.userservice.controller.ConsumerControllerTest, locations = [], classes = [com.learning.microservices.userservice.UserServiceApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper=true"], contextCustomizers = [[ImportsContextCustomizer@26d5a317 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration, org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration, org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration, org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration, org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration, org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration, org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration, org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration, org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration, org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration, org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@2034b64c, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7161d8d1, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@d737b89, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@9da1, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@234914f5, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@e322556a, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@2ab4bc72, org.springframework.boot.test.context.SpringBootTestAnnotation@4ddbcfe0], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
      
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:142)
        at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:127)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:141)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:97)
        at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:241)
        at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:377)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:382)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:377)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
        at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310)
        at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
        at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734)
        at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:376)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:289)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:288)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:278)
        at java.base/java.util.Optional.orElseGet(Optional.java:364)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:277)
        at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:105)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:104)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:68)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
        at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
        at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
        at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
        at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
        at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
        at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
        at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
        at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
        at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
        at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
        at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
      Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'consumerController': Unsatisfied dependency expressed through field 'consumerService': No qualifying bean of type 'com.learning.microservices.userservice.Service.ConsumerService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:712)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:692)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:127)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:481)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1397)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:598)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:915)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
        at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137)
        at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:59)
        at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:47)
        at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1386)
        at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:543)
        at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137)
        at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108)
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:184)
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:118)
        ... 72 more
      Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.learning.microservices.userservice.Service.ConsumerService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1812)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1371)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1325)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:709)
        ... 97 more
      

CodePudding user response:

The difference between @Mock and @MockBean is simply the fact that @MockBean annotates an object, which is injected into the Spring context and used as a bean in the test, and is also a mock. @MockBean extends the functionality of @Mock, which is not related to the Spring framework.

The test of your Service has nothing to do with Spring whatsoever - you can verify that simply by looking at the imports of the class. There is not a single import from the org.springframework package. Using @Mock here is perfectly fine on its own. Combination of MockitoExtension and @InjectMocks takes care of the mocks' lifecycle. The mock instance is created and injected into the test subject (consumerService). Spring context is not created.

In case of the Controller test, you're using Spring-related classes, especially MockMvc and @WebMvcTest - the first is injected automatically thanks to the latter and @WebMvcTest sets up the Spring context for web layer handling. Controller classes are scanned and beans are created, thanks to which the requests performed on MockMvc can access the endpoints. The important part here is that other components (layers) are not scanned and injected automatically. That's the reason the error states:

Error creating bean with name 'consumerController': Unsatisfied dependency (...)

Spring tries to create the controller bean, but the service bean cannot be found. @MockBean notifies Spring that the mock should be created and used as a bean. That's why the test with this annotation works - Spring context can be properly created and the beans can be injected. Some of them (the service) as mocks.

For more information see the description and examples in: Testing the Web Layer in Spring docs.

Additionally, here you can find an example of a spy bean created programmatically using @TestConfiguration. That's similar to using @SpyBean annotation, but can help in some more complicated cases and it's worth noting here. To simplify: @TestConfiguration and mock work similar to @MockBean, which makes Spring tests configuration easier.

  • Related