Our first unit test

It is now time to write our first unit test.

We will focus on writing tests at the controller level because we have little to no business code or service. The key to writing tests for Spring MVC is the org.springframework.boot:spring-boot-starter-test dependency in our classpath. It will add a few very useful libraries, such as these:

  • hamcrest: This is JUnit's assertion library
  • mockito: This is a mocking library
  • spring-test: This is the Spring testing library

We will test the redirection to the profile page that is created when the user hasn't created their profile yet.

We already have an autogenerated test called MasterSpringMvc4ApplicationTests. It is the most basic kind of test one can write with the Spring test framework: it does nothing but blow up if the context cannot be loaded:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MasterSpringMvc4Application.class)
@WebAppConfiguration
public class MasterSpringMvc4ApplicationTests {

    @Test
    public void contextLoads() {
    }
}

We can delete this test and create one that will ensure that a user with no profile will be redirected to the profile page by default. It will actually test the code of the HomeController class, so let's call it HomeControllerTest class and put it in the same package as HomeController, in src/test/java. All IDEs have shortcuts for creating a JUnit test case from a class. Find out how to do it with yours now!

Here is the test:

package masterSpringMvc.controller;

import masterSpringMvc.MasterSpringMvcApplication;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MasterSpringMvcApplication.class)
@WebAppConfiguration
public class HomeControllerTest {
    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void should_redirect_to_profile() throws Exception {
        this.mockMvc.perform(get("/"))
                .andDo(print())
                .andExpect(status().isFound())
                .andExpect(redirectedUrl("/profile"));
    }
}

We use MockMvc to simulate interactions with a Spring controller without the actual overhead of a Servlet container.

We also use a couple of matchers that Spring provides to assert our result. They actually implement Hamcrest matchers.

The .andDo(print()) statement will produce a neat debug output for the request and response of the scenario under test. You can comment it if you find it too verbose.

That's all there is to it! The syntax is a bit tricky at the beginning, but an IDE with good completion will be able to help you.

Now we want to test whether, if the user has filled in the test part of their profile, we can redirect them to the correct search. For that, we will need to stub the session with the MockHttpSession class:

import org.springframework.mock.web.MockHttpSession;
import masterSpringMvc.profile.UserProfileSession;

// put this test below the other one
@Test
public void should_redirect_to_tastes() throws Exception {
    MockHttpSession session = new MockHttpSession();
    UserProfileSession sessionBean = new UserProfileSession();
    sessionBean.setTastes(Arrays.asList("spring", "groovy"));
    session.setAttribute("scopedTarget.userProfileSession", sessionBean);

    this.mockMvc.perform(get("/").session(session))
        .andExpect(status().isFound())
        .andExpect(redirectedUrl("/search/mixed;keywords=spring,groovy"));
}

You will have to add the setTastes() setter to the UserProfileSession bean for the test to work.

There are a lot of mocking utilities for the Servlet environment in the org.springframework.mock.web package.

Note that the attribute representing our bean in session is prefixed by scopedTarget. That's because session beans are proxified by Spring. Therefore, there are actually two objects in the Spring context, the actual bean that we defined and its proxy that will end up in the session.

The mock session is a neat class, but we can refactor the test with a builder that will hide implementation details and can be reused later:

@Test
public void should_redirect_to_tastes() throws Exception {

    MockHttpSession session = new SessionBuilder().userTastes("spring", "groovy").build();
    this.mockMvc.perform(get("/")
        .session(session))
        .andExpect(status().isFound())
        .andExpect(redirectedUrl("/search/mixed;keywords=spring,groovy"));
}

The code for the builder is as follows:

public class SessionBuilder {
    private final MockHttpSession session;
    UserProfileSession sessionBean;

    public SessionBuilder() {
        session = new MockHttpSession();
        sessionBean = new UserProfileSession();
        session.setAttribute("scopedTarget.userProfileSession", sessionBean);
    }

    public SessionBuilder userTastes(String... tastes) {
        sessionBean.setTastes(Arrays.asList(tastes));
        return this;
    }

    public MockHttpSession build() {
        return session;
    }
}

After this refactoring, your test should always pass, of course.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.200.109