'@webMvcTest is not excluding and loading beans marked as @Repository

I've a @RestController which has only one dependency in field @Autowire that dependency is @component, that component Class definition has some autowired fields which are @service and those services have some @repositories.

In this whole flow I've kafka, Quartz, Cassandra and DB2 So when I was creating a Unit test case for my controller, I dont want to setup whole application. so I decided to use @webMvcTest and used @MockBean on my only one dependency of controller class.

But my Test is throwing and exception because its trying to create a Dao bean, which is marked as @repository.

@ActiveProfiles("test")
@WebMvcTest(controllers = MyControllerTest .class)
class MyControllerTest {

    @MockBean
    MyControllerDependency dependency;

    @Autowired
    MockMvc mockMvc;

    @Test
    void test_something() throws Exception {
       assert(true);
    }
}

Here is oversimplified version of code

@Component
class MyControllerDependency { 
    @AutoiWired
    MyCustomService service;
}

@Service
class MyCustomService{

   @Autowired
   MyCustomDao dao;
}

@Repository
class MyCustomDao{
    @Autowired
    private JdbcTemplate template;
}

I'm getting following exception in test.

Exception

***************************
APPLICATION FAILED TO START
***************************

Description:

Field template in com.....MyCustomDao`  required a bean of type 'org.springframework.jdbc.core.JdbcTemplate' that could not be found.

Question is, When I'm using @WebMvcTest slice and already mocking the only required dependency MyControllerDependency then why spring test context is trying to load MyCustomDao which is annotated as @Repository.

I can do integration testing with SpringbootTest & AutoconfigureMockMVC, but for writing Junit test just for controller, I need to use WebMvcTest slice. which is creating a problem.



Solution 1:[1]

Do you have any @ComponentScan("...") annotation active on your @SpringBootApplication?

As described in Spring Boot Reference Documentation:

Another source of confusion is classpath scanning. Assume that, while you structured your code in a sensible way, you need to scan an additional package. Your application may resemble the following code:

@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {

    // ...

}

Doing so effectively overrides the default component scan directive with the side effect of scanning those two packages regardless of the slice that you chose. For instance, a @DataJpaTest seems to suddenly scan components and user configurations of your application. Again, moving the custom directive to a separate class is a good way to fix this issue.

One solution is to create a seperate @Configuration that is annotated with @ComponentScan. When creating a @WebMvcTest the configuration (and its component scan is ignored).

@Configuration
@ComponentScan("com.example.another")
public class DbConfig {
}

Solution 2:[2]

I ran into a similar problem where I want to test only my controller using @WebMvcTest, but the spring context was trying to create nondependent spring beans and was failing as below.

Failed to load ApplicationContext java.lang.IllegalStateException: Failed to load ApplicationContext Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'TestController' defined in file ...

Solution: load only the controller your testing for an example like @ContextConfiguration(classes = DemoController.class). Also, find below a complete sample

@WebMvcTest
@ContextConfiguration(classes = DemoController.class)  
public class DemoControllerTest {    
@Autowired
MockMvc mockMvc;

@MockBean
DemoService demoService;

@Test
public void testGetAllProductCodes_withOutData() throws Exception {
    
when(productCodeService.getAllProductCodes()).thenReturn(new ArrayList<ProductCodes>());
        mockMvc.perform(MockMvcRequestBuilders.get("/services/productCodes")).andExpect(MockMvcResultMatchers.status().isNoContent());
    }
}

}

Solution 3:[3]

This usually happens when you have explicit @ComponentScan annotation on the spring boot main application class.

The @ComponentScan annotation suppresses the default component scanning mechanism that happens with @Webmvctest where it scans up the package hierarchy and apply excludeFilters to find only controller and its related classes.

Solution 4:[4]

When you mock your bean using @MockBean annotation you should define what that mocked bean should do when you call it's method, you usually do this using when in Mockito. In your case it can be done this way:

@ActiveProfiles("test")
@WebMvcTest
class MyControllerTest {

    @MockBean
    MyControllerDependency dependency;

    @Autowired
    MockMvc mockMvc;

    @Test
    void test_something() throws Exception {
        when(dependency.sample()).thenReturn("Hello, Mock");
        mockMvc.perform(get("/api/test/restpoint")
                .accept(MediaType.APPLICATION_JSON))
               .andDo(print())
               .andExpect(status().isOk());
    }
}

At this line:

when(dependency.sample()).thenReturn("Hello, Mock");

Instead of dependency.sample() you should put whatever method of MyControllerDependency class your controller call when you send a GET request to /api/test/restpoint path and with thenReturn("Hello, Mock") you define what is the mocked output of that method (when it gets called by your controller in your Unit test).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 PhilippS
Solution 2 Raj
Solution 3 Kumar V
Solution 4