简体   繁体   中英

Testing a spring boot controller which has dependencies? (jUnit)

I have this test class :

@RunWith(SpringRunner.class)
@WebMvcTest(ClassToBeTested.class)
public class ClassToBeTestedTests {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void simpleTestMethodToGetClassWorking(){
        Assert.assertTrue(true);
    }
}

but in the class I want to test, I have this line :

@Autowired
AnnoyingServiceWhichIsADependency annoyingDependency;

So when I try to run the test class - I get this error :

java.lang.IllegalStateException: Failed to load ApplicationContext

and the cause by line seems to throw this up :

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ClassToBeTested': Unsatisfied dependency expressed through field 'AnnoyingServiceWhichIsADependency'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type '<package-path>.AnnoyingServiceWhichIsADependency' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

I will add that the actual class does work, and does what it is meant to do, but I am having trouble making it work in the unit test world.

All help appreciated.

The reason a bean is not created for the dependency class is that you're using @WebMvcTest and not @SpringBootTest : only controllers and the MVC infrastructure classes are scanned. From the docs :

Can be used when a test focuses only on Spring MVC components.

Since it's an MVC test, you can mock the service dependency. Example: https://reflectoring.io/spring-boot-web-controller-test/

Your test application context is trying to load your ClassToBeTested but is unable to find one of its dependencies and complains about it via that error. Basically you need to have a @Bean of that type in your test context. An option will be to create a TestConfig class which provides a Mock/Spy of that dependency via @Bean annotation. In your test you will have to load inside the context via the @ContextConfiguration annotation this test config you just created.

https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-testing-annotation-contextconfiguration

@WebMvcTest is only going to scan the web layer- the MVC infrastructure and @Controller classes. That's it. So if your controller has some dependency to other beans from, eg form your service layer, they won't be found to be injected.

If you want a more comprehensive integration test, use @SpringBootTest instead of @WebMvcTest

If you want something closer to a unit test, mock your dependency.

Also note that Field injection (@Autowired directly on the field) is not recommended exactly for these reasons. I recommend you change to constructor injeciton ( add a constructor for Classtobetested and place the @Autowired annotation on it. ) Then for a unit test you can pass in a mock. Constructor injection leads to a more testable and configurable design.

Just mock that dependency. Assuming that AnnoyingServiceWhichIsADependency is an interface:

@RunWith(SpringRunner.class)
@WebMvcTest(ClassToBeTested.class)
public class ClassToBeTestedTests {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private AnnoyingServiceWhichIsADependency annoyingDependency;

    @Test
    public void simpleTestMethodToGetClassWorking(){
        Assert.assertTrue(true);
    }
}

Use Mockito when and thenReturn methods to instruct the mock.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM