4

I have a simple spring boot controller and I want to write unit test for it but there is an error. I've googled for hours but still cannot find the solution. Here is code:

HelloController.java

@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @GetMapping("/hello")
    public String sayHello(){
        return helloService.sayHello();
    }
}

HelloService.java:

@Service
public class HelloService {
    public String sayHello(){
        return "Hello";
    }
}

And unit test file: HelloControllerTest.java:

@RunWith(SpringJUnit4ClassRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Mock
    private HelloService helloService;

    @Before
    public void setUp(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void sayHello() throws Exception {
        when(helloService.sayHello()).thenReturn("thach");
        mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string("thach"));
    }

}

But there is an error:

java.lang.IllegalStateException: Failed to load ApplicationContext

    at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:122)
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:105)
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:74)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:312)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:284)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.IllegalArgumentException: Cannot load an ApplicationContext with a NULL 'contextLoader'. Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.
    at org.springframework.util.Assert.notNull(Assert.java:134)
    at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:57)
    at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)
    ... 24 more

Can any one help me ? I am just a newbie to Spring boot

1

5 Answers 5

15

I had similar issue. Please see code below:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class ApplicationControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    ApplicationService applicationService;

    @Test
    public void testGetImagePath() throws Exception {

        RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/application/get-image")
                .contentType(MediaType.IMAGE_GIF);

        MvcResult result = mockMvc.perform(requestBuilder).andReturn();
        System.out.println(result.getResponse());
    }
}

Please see below test case execution:

Test Case Image

Check Annotations you used i.e. :

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. @AutoConfigureMockMvc
Sign up to request clarification or add additional context in comments.

1 Comment

Some cases need to specify classes property for @SpringBootTest annotation
8

For anyone that runs into the same issue - It is important to use @MockBean. (@Mock won't cut it)

See https://spring.io/guides/gs/testing-web/:

We use @MockBean to create and inject a mock for the GreetingService (if you do not do so, the application context cannot start), and we set its expectations using Mockito.

So it is likely all may have been fine if OP just replaced

@Mock
private HelloService helloService;

with

@MockBean
private HelloService helloService;

So the additional class annotations from the accepted answer should not be necessary

Comments

3

if converted into @SpringBootTest it will becomes integration testing. but we want to test only specific controller (unit testing) with @WebMvcTest so how it will become a feasible solution.

1 Comment

agreed but doesnt say how to solve it exactly
0

I had the same problem and tried different ways, but none of them didn't work, Finally, I included two annotations to my Test Class which resolved my problem:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
HelloControllerTest {
}

If you don't want to generate random port to test your API just add the following annotations:

@SpringBootTest
@AutoConfigureMockMvc
HelloControllerTest {
}

Comments

-1

What's missing in here is the @SpringBootTest annotation. This annotation is used to load @Configuration classes for testing and start the embedded container with the default port.
If a @Configuration class is not specified, it would use the default @SpringBootApplication class, since it has @Configuration inside its annotation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.