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

Thach Huynh
  • 1,173
  • 2
  • 12
  • 24
  • Possible duplicate of [Failed to load ApplicationContext when running Integration Tests](https://stackoverflow.com/questions/42127297/failed-to-load-applicationcontext-when-running-integration-tests) – xingbin Jan 14 '18 at 07:15

5 Answers5

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

Shashank Bodkhe
  • 941
  • 10
  • 15
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

Bob
  • 1,351
  • 11
  • 28
Adam
  • 81
  • 1
  • 4
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.

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 {
}
Programmer
  • 57
  • 7
-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.