2

Im trying to do unit tests for a Rest Controller. I did a stub(~mock) for the manager to the database acces and it works well. My only issue is that when I start my unit test it doesn't start the Application.

How can I start the application from my unit test ?

I'm using spring 4.2.3, spring boot 1.3.7, junit 4.12.

Here are my classes :

TestRestController

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(locations = "classpath:/META-INF/spring/mvc/mvc-test-context.xml")
public class RestControllerTest extends AbstractTransitionnalTest {

  @Autowired
  private IManager Manager;

  @Test
  public void getTestSingleItem(){
    Item itm = myTestItemPreInitiallized;
    Manager.save(itm);
    List<Map> apiResponse = restTemplate.getForObject(networkAddress + "/items", List.class);
    // Assertions on apiResponse
  }
}

RestController:

@RestController
@RequestMapping("/items")
class RestController {

  @Autowired
  private IManager Manager;

  // Controller content
}

Beans in mvc-test-context.xml

<bean
        id="IManager"
        class="com.service.ManagerStub">
</bean>
<bean
        id="RestController"
        class="com.controller.RestController">
</bean>

Application class that contains the main

@Configuration
@EnableAutoConfiguration
@EnableTransactionManagement
@ImportResource({ "classpath:/META-INF/spring/context-application.xml" })
public class Application {

If I run it as it is now the application class isn't started and i get the following erreor : I/O error on GET request for adress:Connection refused

If you don't have the exact solution or would like to propose another way to do this or a workaround, what I wish for is to have the ManagerStub to be inserted in the @Autowired manager instead Manager class only when I launch my test.

banetl
  • 199
  • 3
  • 13

3 Answers3

6

We can use the combination of MockitoJUnitRunner and Spring's MockMvcBuilders class to write the unit test the Spring REST Controller.

I have made changed to your code and refer it below to write the JUnits for your REST Controller.

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import com.fasterxml.jackson.databind.ObjectMapper;

import biz.cogitare.framework.controllers.advices.ExceptionControllerAdvice;

@RunWith(MockitoJUnitRunner.class)
public class RestControllerTest {

    private MockMvc mockMvc;

    private Item item;

    private String itemJSON;

    @Mock
    private Manager manager;

    @InjectMocks
    private RestController restController = new RestController();

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

        mockMvc = MockMvcBuilders.standaloneSetup(restController)
                .setMessageConverters(new MappingJackson2HttpMessageConverter());

         Item item = myTestItemPreInitiallized;

        itemJSON = new ObjectMapper().writeValueAsString(itm);
    }

    @Test
    public void testQuerySuccess() throws Exception {

        List<Item> items = new ArrayList<>();
        items.add(item);

        Mockito.when(manager.findItems()).thenReturn(items);

        mockMvc.perform(get("/items?itemId=1").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk());
                //.andExpect(jsonPath("$[0].id", is(1)))
                //.andExpect(jsonPath("$[0].name", is("xyz")));

        Mockito.verify(manager).findItems();
    }


    @Test
    public void testInsertSuccess() throws Exception {

        Mockito.when(manager.insertOrUpdate(Mockito.any(Item.class))).thenReturn(item);

        mockMvc.perform(post("/items").contentType(MediaType.APPLICATION_JSON).content(itemJSON)
                .accept(MediaType.APPLICATION_JSON)).andExpect(status().isCreated());

        Mockito.verify(manager).save(Mockito.any(Item.class));
    }
}
Vasu
  • 21,832
  • 11
  • 51
  • 67
1

I annotate my integrated REST JUnit tests with:

@WebIntegrationTest

And the actual Application class with

@SpringBootApplication

(on top of all the annotations that you showed).
Using those annotations, Spring Boot takes care of launching the application with the provided configuration before running the tests.

EDIT: According to the documentation, @WebIntegrationTest is deprecated since 1.4 in favor of @SpringBootTest, but you're using 1.3.7 so no problem.

walen
  • 7,103
  • 2
  • 37
  • 58
  • When I add the annotation ```@WebIntegrationTest``` I get the following exception ```java.lang.IllegalStateException: Failed to load ApplicationContext```. I also tried to manually load the application context and it still does not start the main application with these annotations. It may because the context is loaded manually, or there is something I forgot in the context file. – banetl Oct 28 '16 at 08:11
  • Did you use both annotations or just the first one? – walen Oct 28 '16 at 08:18
  • I tried using the first one and both at the same time, leads to the same result. I also tried removing the inheritance from `AbstractTransitionnalTest` which has the annotation `@Transactionnal` and I still got the same exception – banetl Oct 28 '16 at 08:56
0

I'm trying to answer your question using Mockito and Junit with MockMvc Testing method.

  • Here I've make some changes in your present test class.

TestRestController

import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(locations = "classpath:/META-INF/spring/mvc/mvc-test-context.xml")
    public class RestControllerTest extends AbstractTransitionnalTest {

      @Mock
      private IManager Manager;

        private MockMvc mockMvc;

        @Before
        public void setUp() throws Exception {
            initMocks(this);// this is needed for inititalization of mocks, if you use @Mock 
            RestController controller = new RestController(manager);
            mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
        }

      @Test
      public void getTestSingleItem(){
        Item itm = yourTestItemPreInitiallized;
        Mockito.when(manager.save(Mockito.any(Item.class))).thenReturn(itm);

        mockMvc.perform(MockMvcRequestBuilders.post("/items")
                    .content(asJsonString(app))
                    .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("application/json;charset=UTF-8"));
        }

        public static String asJsonString(final Object obj) {
            try {
                return new ObjectMapper().writeValueAsString(obj);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
Ravi Mengar
  • 1,181
  • 1
  • 11
  • 18