Junit testing with Spring

Testing code is an important step as writing code. Unfortunately this task is frequently put at last position, too near the project deadline and,  for that reason, it stays unimplemented.

In this post I’m not going to argue about the project timeline but I’d like to discuss of how test the solution when not all the resource are available at the developer’s environment.

Starting from an old post from Spring’s Blog (https://spring.io/blog/2011/06/21/spring-3-1-m2-testing-with-configuration-classes-and-profiles) , I’d like to introduce you at the Unit Test using Junit, Spring (Mvc) and Mockito.

Let me introduce the issue. You’re a good developer, you’ve written your code and now you’re writing the Junit test case to verify it.

Unfortunately, not every resources are available on your local solution. Typically, that happens for database connection or authentication system.
You could use a local database (HSql) to check the Dao Layer, but it could be useless, for example, when your solution uses Oracle Stored Procedure. Also, you could be not able to recreate the entire dataset on your database, so, your Junit test might be unreliable.

The main point is to update your as less as possible to check your solution, so, you need to Mock the not available resources in order to check your code.

We’re going to test a Mvc solution like this:

@Controller
public class WebController {

@Autowired
SqlDao jdbcDao;

@RequestMapping(method = RequestMethod.GET, value = "/lastname")
public String getLastName(@RequestParam String firstname, ModelMap model) throws InterruptedException {

  TableData tableData = jdbcDao.getData(firstname);

  if (tableData!=null)
    model.addAttribute("lastname", tableData.getLast_name());
  else
    model.addAttribute("lastname", "");
  return "index";

}

@RequestMapping(method = RequestMethod.GET, 
value = "/lastnamejson", produces = "application/json")
@ResponseBody
public Customer getLastNameJson(@RequestParam String firstname, ModelMap model) throws InterruptedException {

  TableData tableData = jdbcDao.getData(firstname);

  Customer customer = new Customer();
  customer.setId(tableData.getId());
  customer.setName(tableData.getLast_name());

  return customer;

}
}

The same information are available in a Html page and in a Json Format.

The Spring configuration file is:


<context:component-scan base-package="it.blog.springjunit.controller" />
<context:annotation-config />

<!-- Json Parse -->
<mvc:annotation-driven />
<context:property-placeholder location="classpath:springjunittest.properties" />

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>

<beans profile="production">

<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" p:maxActive="8" p:initialSize="8" />

<!-- Database initializer. If any of the script fails, the initialization stops. -->
<!-- As an alternative, for embedded databases see <jdbc:embedded-database/>. -->
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="${jdbc.initLocation}"/>
<jdbc:script location="${jdbc.dataLocation}"/>
</jdbc:initialize-database>

<bean id="jdbcBo" class="it.blog.springjunit.dao.SqlDao">
<constructor-arg ref="dataSource"></constructor-arg>

</bean>
</beans>

Pay attention at the highlight row. The “production” profile is defined for the dataSource bean. So, only this profile will access at these components once the configuration file has been loaded.

The Dao layer is:

public class SqlDao extends JdbcDaoSupport {
 
 public SqlDao(){}
 
 public SqlDao(DataSource dataSource){
  setDataSource(dataSource);
 }
 
 public TableData getData(String firstName) {
  @SuppressWarnings({ "unchecked", "rawtypes" })
  List<TableData> tableDatas = getJdbcTemplate().query(
    "SELECT * FROM Customers WHERE first_name= ?", new Object[] { firstName },
    new BeanPropertyRowMapper(TableData.class));
  /*
   * Record Exists
   */
  if (tableDatas.size()>0)
   return tableDatas.get(0);
  else
   return null;
 } 
}

And TableData class


public class TableData {
private int id;
private String first_name;
private String last_name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
}

What I’d like to test is the Mvc RequestMapping method defined in WebController without have the Dao layer available.

I’ll illustrate two options; the first using directly the controller “WebController” and the second using MockMvc class.

The first is the following:

@ContextConfiguration
@ActiveProfiles("dev")
@RunWith(SpringJUnit4ClassRunner.class)
public class MockitoTest {

@Autowired
SqlDao jdbcDao;

@Autowired
WebController controller;

@Configuration
@ImportResource(value = {"classpath:springjunittest-servlet.xml"})
static class TestConfig {
@Bean
public SqlDao jdbcDao() {
return Mockito.mock(SqlDao.class);
}
}

@Test
public void testDataDb() throws InterruptedException
{
String firstName = "Larry";
String lastName = "Bird";

TableData tableData = new TableData();

tableData.setFirst_name(firstName);
tableData.setLast_name(lastName);

when(jdbcDao.getData(firstName)).thenReturn(tableData);

ModelMap model = new ModelMap();

controller.getLastName(firstName, model);

assertEquals(model.get("lastname"), lastName);
}
}

Do you remember the highlighted row in the configuration file? In this class I defined the profile as @ActiveProfiles(“dev”) and this means that not Dao is loaded from the configuration file.

However, we need it to test the solution, so we declare the SqlDao as Mock Object loaded by the @Configuration annotation where the original Spring configuration file is loaded.

Not bad! I haven’t changed anything from my original Spring configuration file and I’ve avoided to load the DataSource. Once the solution will be deployed, the ActiveProfile used is the one declared in web.xml

<servlet>
<servlet-name>springjunittest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>production</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springjunittest</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

The highlight rows declare the Active Profile as “production”.

The second option is made by using Spring MockMvc class from the package org.springframework.test.web.servlet.


@WebAppConfiguration
@ContextConfiguration
@ActiveProfiles("dev")
@RunWith(SpringJUnit4ClassRunner.class)
public class MockMvcTest {

private MockMvc mockMvc;

@Autowired
private WebApplicationContext webAppContext;

@Autowired
SqlDao jdbcDao;

@Configuration
@ImportResource(value = {"classpath:springjunittest-servlet.xml"})
static class TestConfig {
@Bean
public SqlDao jdbcDao() {
return Mockito.mock(SqlDao.class);
}
}

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

String firstName = "James";
String lastName = "Dean";

TableData tableData = new TableData();

tableData.setFirst_name(firstName);
tableData.setLast_name(lastName);

when(jdbcDao.getData(firstName)).thenReturn(tableData);
}
@Test
public void findUser()
throws Exception {

RequestBuilder request = get("/lastname").param("firstname", "James").contentType(MediaType.TEXT_HTML);

mockMvc.perform(request)
.andExpect(status().isOk())
.andExpect(model().attribute("lastname", "Carter"))
.andDo(print());
}

@Test
public void findUserJson()
throws Exception {

RequestBuilder request = get("/lastnamejson").param("firstname", "James").contentType(MediaType.TEXT_HTML);

mockMvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"))
.andExpect(jsonPath("name").value("Carter"))
.andDo(print());
}
}

The core of this class is the “perform” method of the mockMvc object. Briefly, the test calls the path “/lastnamejson” passing the “firstname” parameter as Html/text request.

Once the url has been called, the method check these steps:

  • The method return http status 200;
  • The content type is application/json;
  • The response Json includes the node “name” with value “Carter”.

Finally, it prints the operation result.

A complete set of “andExpected” parameter accepted are available in the package org.springframework.test.web.servlet.result

Conclusion.

Obviously, this is not the only existing method to test a solution code. There’re a lot of different methods to test the code in order to achieve different test scopes (Controller test, Integration Test, Service test, …).

The choice depends of the aim of the test. Different type of tests could be available for the same controller, this is completely understandable in every type of solution.

The complete solution https://github.com/MarcoGhise/SpringJunitTest

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s