Thursday, 11 February 2016

Configuring Spring-Test-Mvc

By

This blog is in continuation of my previous blog JUnit if you haven't read it please read it before this blog
  • The heart of the spring-test-mvc is a class called MockMvc that can be used to write tests for any application implemented by using Spring MVC.
  • A Mock Object is an object that substitutes for a real object.
A computer programmer typically creates a mock object to test the behavior of some other object
Mock objects allow you to set up test scenarios without bringing to bear large, unwieldy resources such as databases. Instead of calling a database for testing, you can simulate your database using a mock object in your unit tests. This frees you from the burden of having to set up and tear down a real database, just to test a single method in your class.

The purpose of mocking is to allow you to test your application components in isolation. For example, if you have a service that uses a DAO, you want to be able to test your service without actually having to go to the database via the DAO. You would
1) Test the DAO in isolation, and
2) Mock the DAO in your service tests, so your service can be tested in isolation.

Since your DAO has a clearly defined API, your mock DAO simulates that API. So if you have a findAll type method that your service calls, you can easily mock the DAO to expect a call to findAll and return the appropriate results.
Our goal is to create a new MockMvc object by using the implementations of the MockMvcBuilder interface.
The MockMvcBuilders class has four static methods which we can use to obtain an implementation of the MockMvcBuilder interface. These methods are described in following:

  • ContextMockMvcBuilder annotationConfigSetup (Class… configClasses) method must be used when we are using Java configuration for configuring the application context of our application.
  • MockMvc mockMvc = MockMvcBuilders.xmlConfigSetup("classpath:applicationContext.xml").build();
  • ContextMockMvcBuilder xmlConfigSetup (String… configLocations) must be used when the application context of our application is configured by using XML configuration files.
  • MockMvc mockMvc = MockMvcBuilders.annotationConfigSetup(ExampleApplicationContext.class).build();
  •   StandaloneMockMvcBuilder standaloneSetup (Object… controllers) must be used when we want to configure the tested controller and the required MVC components manually.
  • MockMvc mockMvc = MockMvcBuilders.standaloneSetup(newHomeController()).build();
  • InitializedContextMockMvcBuilder webApplicationContextSetup (WebApplicationContext context)must be used when we have already created a fully initialized WebApplicationContext object.
  • MockMvc mockMvc = MockMvcBuilders.webApplicationContextSetup(wac).build();
Required Utility Classes

  • The GenericWebContextLoader is a class that provides a support for creatingWebApplicationContext objects.
  • The WebContextLoader class is an application specific extension to the GenericWebContextLoaderclass, and it provides us access to the WebApplicationContext of our application.
Putting It All Together
We can configure our integration tests by following these steps:

  • Use the @RunWith annotation to configure the used test runner. In this case we must use theSpringJUnit4ClassRunner class to run our integration tests.
    • @RunWith(SpringJUnit4ClassRunner.class)
  • Use the @ContextConfiguration annotation to configure either the application context configuration class or the xml configuration file. Set the value of its loader property to WebContextLoader.class.
    • @ContextConfiguration(loader = WebContextLoader.class, classes =     {ExampleApplicationContext.class})
  • Add WebApplicationContext field to our test class and annotate it with the @Resource annotation. This field contains a reference to the used web application context.
  • Use the @DatabaseSetup annotation to specify the DBUnit dataset file that is used to initialize our database to a known state before tests are run. We can skip this test if our tests are not using database.
  • Add a MockMvc field to our test class. This field contains a reference to the MockMvc object that is used in our integration tests.
  • Create a public setUp() method and annotate this method with the @Before annotation. This method creates a new MockMvc object by using the static webApplicationContextSetup() method of the MockMvcBuilders class.
Submit an empty add todo form

    @Test
    @ExpectedDatabase("toDoData.xml")
    public void addEmptyTodo() throws Exception {                                                                                                   mockMvc.perform(post("/todo/add").contentType
                 (MediaType.APPLICATION_FORM_URLENCODED).sessionAttr("todo", new                                  TodoDTO()) ).andExpect(status().isOk()).andExpect(view().name("todo/add"))                                    .andExpect(forwardedUrl("/WEB-INF/jsp/todo/add.jsp"))                                                                      .andExpect(model().attributeHasFieldErrors("todo", "title"))                                                                  .andExpect(model().attribute("todo",hasProperty("id",nullValue())))                                      .andExpect(model().attribute("todo",hasProperty("description",isEmptyOrNullString())))                .andExpect(model().attribute("todo", hasProperty("title", isEmptyOrNullString())));
}

Submit Add Todo Form With Validation Errors

 @Test
 @ExpectedDatabase("toDoData.xml")
    public void addTodoWhenTitleAndDescriptionAreTooLong() throwsException
{
  String title = TodoTestUtil.createStringWithLength(101);
  String description =       TodoTestUtil.createStringWithLength(501);
  mockMvc.perform(post("/todo/add")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("descryption", description).param("title", title)
.sessionAttr("todo", new TodoDTO())).andExpect(status().isOk())                .andExpect(view().name("todo/add")).andExpect(forwardedUrl("/WEB-INF/jsp/todo/add.jsp"))            .andExpect(model().attributeHasFieldErrors("todo", "title"))                .andExpect(model().attributeHasFieldErrors("todo","description"))                .andExpect(model().attribute("todo",hasProperty("id",nullValue())))                .andExpect(model().attribute("todo",hasProperty("description",is(description))))                .andExpect(model().attribute("todo", hasProperty("title",is(title))));
 }

Submit Add Todo Form
@Test 
@ExpectedDatabase(value="toDoData-add-expected.xml", assertionMode = DatabaseAssertionMode.NON_STRICT)
   public void addTodo() throws Exception {
   mockMvc.perform(post("/todo/add")                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("description", "description").param("title", "title")
.sessionAttr("todo", new TodoDTO())).andExpect(status().isOk())                .andExpect(view().name("redirect:/todo/view/{id}"))
.andExpect(model().attribute("id", is("3")))
.andExpect(flash().attribute("feedbackMessage", is("Todo entry:title was added.")));
}

Result
1
2
3
4
5
<dataset>
    <todos id="1" description="Lorem ipsum" title="Foo" version="0"/>
    <todos id="2" description="Lorem ipsum" title="Bar" version="0"/>
    <todos id="3" description="description" title="title" version="0"/>
</dataset>

Show Update Todo Form
@Test
@ExpectedDatabase("toDoData.xml")
public void showUpdateTodoForm() throws Exception {        mockMvc.perform(get("/todo/update/{id}", 1L)).andExpect(status().isOk())                .andExpect(view().name("todo/update")).andExpect(forwardedUrl("/WEB-INF/jsp/todo/update.jsp"))  .andExpect(model().attribute("todo", hasProperty("id", is(1L))))                .andExpect(model().attribute("todo", hasProperty("description",is("Lorem ipsum"))))                .andExpect(model().attribute("todo", hasProperty("title", is("Foo"))));
}

Show Update Todo Form When The Todo Entry Is Not Found
@Test
@ExpectedDatabase("toDoData.xml")
  public void showUpdateTodoFormWhenTodoIsNotFound() throwsException {                                     mockMvc.perform(get("/todo/update/{id}", 3L)).andExpect(status().isNotFound())                .andExpect(view().name("error/404")).andExpect(forwardedUrl("/WEB-INF/jsp/error/404.jsp"));
}

Show Add Todo Form
@Test
@ExpectedDatabase("toDoData.xml")
    public void showAddTodoForm() throws Exception {                                                                             mockMvc.perform(get("/todo/add")).andExpect(status().isOk())                .andExpect(view().name("todo/add")).andExpect(forwardedUrl("/WEB-INF/jsp/todo/add.jsp"))         .andExpect(model().attribute("todo",hasProperty("id", nullValue())))                .andExpect(model().attribute("todo", hasProperty("description",                           isEmptyOrNullString()))).andExpect(model().attribute("todo", hasProperty("title", isEmptyOrNullString())));
  }
}

Todo Entries Are Found
@Test
@ExpectedDatabase("toDoData.xml")
    public void findAll() throws Exception {
    mockMvc.perform(get("/")).andExpect(status().isOk())                .andExpect(view().name("todo/list")).andExpect(forwardedUrl("/WEB-INF/jsp/todo/list.jsp"))           .andExpect(model().attribute("todos", hasSize(2))).andExpect(model().attribute("todos", hasItem(        allOf(hasProperty("id", is(1L)),hasProperty("description", is("Lorem ipsum")),                               hasProperty("title", is("Foo")))))).andExpect(model().attribute("todos", hasItem(allOf( hasProperty("id", is(2L)),hasProperty("description", is("Lorem ipsum")),                                hasProperty("title", is("Bar"))))));
   }
}

Todo Entry Is Added To The Database
@Test
public void add_NewTodoEntry_ShouldAddTodoEntryAndRenderViewTodoEntryView() throws Exception {
        Todo added = new TodoBuilder().id(1L).description("description").title("title").build();
        when(todoServiceMock.add(isA(TodoDTO.class))).thenReturn(added);
        mockMvc.perform(post("/todo/add")
       .contentType(MediaType.APPLICATION_FORM_URLENCODED)
       .param("description", "description")
       .param("title", "title")
       .sessionAttr("todo", new TodoDTO()))
       .andExpect(status().isMovedTemporarily())
       .andExpect(view().name("redirect:todo/{id}"))
       .andExpect(redirectedUrl("/todo/1"))
       .andExpect(model().attribute("id", is("1")))
       .andExpect(flash().attribute("feedbackMessage", is("Todo entry: title was added.")));
       ArgumentCaptor<TodoDTO> formObjectArgument =                                                                            ArgumentCaptor.forClass(TodoDTO.class);
       verify(todoServiceMock, times(1)).add(formObjectArgument.capture());
       verifyNoMoreInteractions(todoServiceMock);
       TodoDTO formObject = formObjectArgument.getValue();
       assertThat(formObject.getDescription(), is("description"));
       assertNull(formObject.getId());
       assertThat(formObject.getTitle(), is("title"));
    }
}


0 comments :

Post a Comment