0

I've written a couple of JUnit tests to test my REST functionality. Since I only want to test REST (and not the database, domain logic, ..) I made a stub filles with dummy data which stands for the rest of the backend.

[EDIT] For example I want to test /customers/all A GET request will be responded to with an arraylist containing all names.

I therefore use MockMV.

    this.mockMvc.perform(get("/customers/all").accept("application/json"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$").isNotEmpty())
            .andExpect(jsonPath("$[0].name", is("John")));

When you normally perform a GET request towards /customers/all a request will be sent to the database. Now, to test my REST controller I made a stub which responds to GET /customers/all with a simple arraylist containing just my name (as you can see in the test). When I test this local I simply replace the real class with this stub. How is this done dynamically?

Ricardo
  • 335
  • 1
  • 4
  • 13
  • Controllers are tested like any other class, except for the fact that they are generally dumb and would delegate a big amount of work to other classes. What you want to do is to mock all the other classes involved in the processing and in relationship with the controller. So, to answer your question I would say that it seems to me that your approach seems wrong. Could you please clarify what stub you are talking about? – LoreV Apr 08 '16 at 09:19
  • I use MockMVC to test my REST. If I dont use a stub for the rest of my backend every test I do with MockMVC will perform a request towards the rest of my backend, isn't it? – Ricardo Apr 08 '16 at 09:25
  • MockMvc is used for Integration tests, also called End2End tests. These have the purpose of testing more than a unit of your code. See http://www.petrikainulainen.net/programming/spring-framework/integration-testing-of-spring-mvc-applications-configuration/. However, following your question, I would recommend using Mockito to create unit tests for your controller before continuing on with the MockMVC integration framework. – LoreV Apr 08 '16 at 09:31
  • 1
    @LoreV no. MockMvc can also be used with a single controller, in isolation, whose dependencies are mocked. I do that all the time to test the mapping annotations, the marshalling, etc. See http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#spring-mvc-test-server-setup-options – JB Nizet Apr 08 '16 at 19:25

1 Answers1

2

I don't think your approach is the good one. Just use your real controller, but stub its dependencies (using Mockito, for example), just like you would do for a traditional unit test.

Once you have an instance of the controller using stubbed dependencies, you can use a standalone setup and use MockMvc to test, in addition to the controller code, the mapping annotations, the JSON marshalling, etc.

Thias approach is described in the documentation.

Example using Mockito, assuming the controller delegates to a CustomerService:

public class CustomerControllerTest {

    @Mock
    private CustomerService mockCustomerService;

    @InjectMocks
    private CustomerController controller;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void shouldListCustomers() {
        when(mockCustomerService.list()).thenReturn(
            Arrays.asList(new Customer("John"),
                          new Customer("Alice")));

        this.mockMvc.perform(get("/customers").accept("application/json"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$").isNotEmpty())
            .andExpect(jsonPath("$[0].name", is("John")));
    }
}
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • I am using my real REST controllers. All my REST controllers communicate with an interface. This interface is the gateway to my domain logic. Since I have several REST controllers it would be much easier to just - somehow - replace this interface with a stub. Is this what you're saying? – Ricardo Apr 08 '16 at 19:53
  • Well, that's what the above does: it replace the CustomerService with a stub. But the stub is not global to the application. It's specific to the test, and returns the values needed for that test. Maybe another test should check that the controller truncates the list to 1000 elements if the service returns more than 1000 customers. No problem: just write another test where the service is stubbed to return 10001 customers. – JB Nizet Apr 08 '16 at 19:58
  • Because Mockito is a mocking framework, that allows creating stubs/mocks dynamically, without you having to explicitly write a fake implementation of the interface to stub. – JB Nizet Apr 08 '16 at 20:12