400+ Java Interview Questions and Answers blog

400+ Java Interview Questions and Answers blog


Unit testing Spring MVC controllers for web and RESTful services with spring-test-mvc

Posted: 12 Dec 2012 04:20 PM PST

As Web application and RESTful web services are very common in enterprise applications, it is imperative to unit test the controllers. There are a number of strategies to unit test your controllers in an MVC framework. For example, running an embedded web server like jetty, and then run the unit tests against the web server, etc. The spring-test-mvc project makes testing your Spring MVC controllers very easy without starting an embedded server. This blog post will take you through the key steps involved.


Step 1: You need to have the right third-party libraries. The key ones to take note are spring-test-mvc, spring-test, json-path, hamcrest-library, hamcrest-core, and mockito-core.

           <dependency>     <groupId>org.mockito</groupId>     <artifactId>mockito-core</artifactId>     <scope>test</scope>     <exclusions>      <exclusion>       <groupId>org.hamcrest</groupId>       <artifactId>hamcrest-core</artifactId>      </exclusion>     </exclusions>    </dependency>        <dependency>     <groupId>org.hamcrest</groupId>     <artifactId>hamcrest-library</artifactId>     <version>1.3</version>    </dependency>        <dependency>     <groupId>org.codehaus.jackson</groupId>     <artifactId>jackson-mapper-asl</artifactId>     <version>1.9.8</version>     <scope>runtime</scope>    </dependency>      <dependency>     <groupId>javax.xml.bind</groupId>     <artifactId>jaxb-api</artifactId>     <version>2.2.6</version>     <scope>runtime</scope>    </dependency>      <!-- JSON XPATH library -->    <dependency>     <groupId>com.jayway.jsonpath</groupId>     <artifactId>json-path</artifactId>     <version>0.5.6</version>    </dependency>        <!-- spring -->        <dependency>     <groupId>org.springframework</groupId>     <artifactId>org.springframework.context</artifactId>     <version>3.1.0.RELEASE</version>     <exclusions>      <!-- Exclude Commons Logging in favour of SLF4j -->      <exclusion>       <groupId>org.apache.commons</groupId>       <artifactId>com.springsource.org.apache.commons.logging</artifactId>      </exclusion>     </exclusions>    </dependency>    <dependency>     <groupId>org.springframework</groupId>     <artifactId>spring-aop</artifactId>     <version>3.1.0.RELEASE</version>    </dependency>    <dependency>     <groupId>org.springframework</groupId>     <artifactId>spring-aspects</artifactId>     <version>3.1.0.RELEASE</version>    </dependency>    <dependency>     <groupId>org.springframework</groupId>     <artifactId>spring-beans</artifactId>     <version>3.1.0.RELEASE</version>    </dependency>    <dependency>     <groupId>org.springframework</groupId>     <artifactId>spring-core</artifactId>     <version>3.1.0.RELEASE</version>     <exclusions>      <exclusion>       <artifactId>commons-logging</artifactId>       <groupId>commons-logging</groupId>      </exclusion>     </exclusions>    </dependency>        <!-- for tspring testing -->    <dependency>     <groupId>org.springframework</groupId>     <artifactId>spring-test</artifactId>     <version>3.1.0.RELEASE</version>    </dependency>        <dependency>     <groupId>org.springframework</groupId>     <artifactId>spring-test-mvc</artifactId>     <version>1.0.0.M1</version>    </dependency>              
Step 2: The next step is to define a Spring MVC controller that we will be writing unit test for.

   package com.myapp.accounting.aes.securities.pricehistory.controller;    import java.sql.SQLException;  import java.util.Date;  import java.util.List;    import javax.annotation.Resource;  import javax.servlet.http.HttpServletResponse;    import org.slf4j.Logger;  import org.slf4j.LoggerFactory;  import org.springframework.format.annotation.DateTimeFormat;  import org.springframework.http.HttpStatus;  import org.springframework.jdbc.CannotGetJdbcConnectionException;  import org.springframework.security.authentication.LockedException;  import org.springframework.stereotype.Controller;  import org.springframework.web.bind.annotation.ExceptionHandler;  import org.springframework.web.bind.annotation.RequestMapping;  import org.springframework.web.bind.annotation.RequestMethod;  import org.springframework.web.bind.annotation.RequestParam;  import org.springframework.web.bind.annotation.ResponseBody;  import org.springframework.web.bind.annotation.ResponseStatus;    import com.jpmorgan.accounting.aes.securities.pricehistory.model.PriceHistory;  import com.jpmorgan.accounting.aes.securities.pricehistory.service.PriceHistoryService;  import com.jpmorgan.accounting.dm.model.refdata.securities.MarketDataSecurityPriceHist;        @Controller  public class PriceHistoryController {         /**       * Service for accessing the Market Data repository. Contains the business logic.       */      @Resource(name = "aes_priceHistoryService")      private PriceHistoryService securityPriceService;       @RequestMapping(value = "/pricehistory",                    method = RequestMethod.GET)   @ResponseBody   public List<MarketDataSecurityPriceHist> getPriceHistory(@RequestParam(value="latestUpdatedTimeStamp", required=true) @DateTimeFormat(pattern="dd MMM yyyy HH:mm:ss") Date latestUpdatedTimeStamp,                                                       @RequestParam(value="maxRecords", required=false, defaultValue="100") int maxRecords) throws Exception    {         // call the business service       PriceHistory result = securityPriceService.getPriceHistory(maxRecords, latestUpdatedTimeStamp);               return result.getHistory();   }  }    

Step 3: The PriceHistory object that holds a list of "MarketDataSecurityPriceHist" objects.

   package com.myapp.accounting.aes.securities.pricehistory.model;    import com.myapp.accounting.dm.model.refdata.securities.MarketDataSecurityPriceHist;  import java.io.Serializable;  import java.util.ArrayList;  import java.util.List;    import javax.xml.bind.annotation.XmlElement;  import javax.xml.bind.annotation.XmlElementWrapper;  import javax.xml.bind.annotation.XmlRootElement;    @XmlRootElement  public class PriceHistory implements Serializable  {      private static final long serialVersionUID = 1L;              private List history = new ArrayList();        @XmlElementWrapper(name = "historicalprices")      @XmlElement(name = "price")      public List getHistory()      {          return history;      }     public void setHistory(List history) {    this.history = history;   }  }  


Step 4: Define the MarketDataSecurityPriceHist class that holds the relevant attributes.

   package com.myapp.accounting.dm.model.refdata.securities;    import java.io.Serializable;  import java.math.BigDecimal;  import java.util.Date;    import javax.persistence.Entity;  import javax.xml.bind.annotation.XmlElement;    @Entity //persistence entity  public class MarketDataSecurityPriceHist implements Serializable   {   private static final long serialVersionUID = 1444498748039607997L;        private String securityCd;      private Date priceDttm;      private BigDecimal bidPrice;      private BigDecimal closePrice;      private BigDecimal lastPrice;      private BigDecimal askPrice;      private String lastUpdatedAction;      private String LastUpdatedDetailUser;      private Date lastUpdatedTimeStamp;      private Date loadTimeStamp;      private String pricecondition;      private String recordType;      private String priceTime;      private String source;         public MarketDataSecurityPriceHist()  {}     @XmlElement   public String getSecurityCd()    {    return securityCd;   }     public void setSecurityCd(String securityCd)    {    this.securityCd = securityCd;   }     @XmlElement   public Date getPriceDttm() {    return priceDttm;   }     public void setPriceDttm(Date priceDttm) {    this.priceDttm = priceDttm;   }      @XmlElement   public BigDecimal getBidPrice() {    return bidPrice;   }     public void setBidPrice(BigDecimal bidPrice) {    this.bidPrice = bidPrice;   }     //other setters and getters are omitted   }      
Step 5: Finally, and most importantly the unit test class that uses mockito to mock the PriceHistoryService implementation and spring-test-mvc to mock the controller that returns a json response.

   package com.myapp.accounting.aes.securities.pricehistory;    import static org.mockito.Mockito.mock;  import static org.mockito.Mockito.when;  import static org.springframework.test.web.server.request.MockMvcRequestBuilders.get;  import static org.springframework.test.web.server.setup.MockMvcBuilders.standaloneSetup;  import static org.springframework.test.web.server.result.MockMvcResultMatchers.*;    import java.math.BigDecimal;  import java.util.ArrayList;  import java.util.Calendar;  import java.util.Date;  import java.util.List;    import org.junit.Before;  import org.junit.Test;  import org.mockito.Mockito;  import org.springframework.http.MediaType;    import com.myapp.accounting.aes.securities.pricehistory.controller.PriceHistoryController;  import com.myapp.accounting.aes.securities.pricehistory.model.PriceHistory;  import com.myapp.accounting.aes.securities.pricehistory.service.PriceHistoryService;  import com.myapp.accounting.aes.securities.pricehistory.service.impl.PriceHistoryServiceImpl;  import com.myapp.accounting.dm.model.refdata.securities.MarketDataSecurityPriceHist;    public class PriceHistoryController2Test {     private PriceHistoryService mockSecurityPriceService;   private PriceHistoryController controller;     @Before   public void setup() {    controller = new PriceHistoryController();    mockSecurityPriceService = mock(PriceHistoryServiceImpl.class);    controller.setSecurityPriceService(mockSecurityPriceService);   }     @Test   public void getPriceHistory() throws Exception {      when(      mockSecurityPriceService.getPriceHistory(        (Integer) Mockito.any(), (Date) Mockito.any()))      .thenReturn(getPriceHistoryTestData());      standaloneSetup(controller)      .build()      .perform(        get(          "/pricehistory?latestUpdatedTimeStamp=26 Jul 211 12:00:00&maxRecords=3000")          .accept(MediaType.APPLICATION_JSON))      .andExpect(status().isOk())      .andExpect(content().type("application/json"))      .andExpect(jsonPath("$.[0].securityCd").value("test"))      .andExpect(jsonPath("$.[0].bidPrice").value(12.50))            .andExpect(jsonPath("$.[0].closePrice").value(12.50));              }     //mock data that gets returned when getPriceHistory(...) method is called   private static PriceHistory getPriceHistoryTestData() {      PriceHistory ph = new PriceHistory();    List<marketdatasecuritypricehist> history = new ArrayList<marketdatasecuritypricehist>();    MarketDataSecurityPriceHist md = new MarketDataSecurityPriceHist();    md.setSecurityCd("test");    BigDecimal price = new BigDecimal("12.50");    md.setAskPrice(price);    md.setBidPrice(price);    md.setClosePrice(price);    md.setLastUpdatedAction("SOME_ACTION");    md.setRecordType("RECORD_TYPE");    md.setLastUpdatedDetailUser("tesr");    Calendar cal = Calendar.getInstance();    cal.set(2010, 01, 01, 00, 00, 00);    Date date = cal.getTime();    md.setLastUpdatedTimeStamp(date);    md.setPriceDttm(date);    System.out.println(date);        history.add(md);    ph.setHistory(history);      return ph;   }    }  

That's all to it for testing an MVC controller. The URL for above JSON RESTful web service will be something like

   http://localhost:8080/accounting-server/securities/pricehistory?latestUpdatedTimeStamp=26+Jul+2010+12:00:00&maxRecords=3000  

and the JSON data returned will be something like

   [{"securityCd":"XX123","priceDttm":"2012-01-28","bidPrice":125.50,"closePrice":126.60,"lastPrice":124.50,"askPrice":123.80,"lastUpdatedAction":"NO ACTION","lastUpdatedTimeStamp":1327705288015,"loadTimeStamp":88150,"pricecondition":"NO_CONDITION","recordType":null,"priceTime":"10:01:28.150 AM","source":"HiPort","lastUpdatedDetailUser":"arul"},  {"securityCd":"YY321","priceDttm":"2012-01-28","bidPrice":125.50,"closePrice":126.60,"lastPrice":124.50,"askPrice":123.80,"lastUpdatedAction":"NO ACTION","lastUpdatedTimeStamp":1327705288015,"loadTimeStamp":88150,"pricecondition":"NO_CONDITION","recordType":null,"priceTime":"10:01:28.150 AM","source":"HiPort","lastUpdatedDetailUser":"arul"}]  






Post a Comment