1. 程式人生 > >基於Spring MVC(REST API)做單元測試(mockito)

基於Spring MVC(REST API)做單元測試(mockito)

最近在公司用的Spring Mvc REST API框架做了一個專案,並且做了基於Spring的單元測試,今天先講一下基於Spring框架的單元測試,測試使用的是Spring自帶的test元件,再結合Mockito一起編寫測試案例,以下示例會包括Controller和Service,由於Repository沒有自己的邏輯,所以這裡就不涉及Repository的單元測試。

首先看一下RestController的程式碼:

package com.dhb.springmvc.controller;

import com.dhb.springmvc.entity.User;
import com.dhb.springmvc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


/**
 * Created by ${denghb} on 2016/7/31.
 */
@RestController
@RequestMapping("/springmvc")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/{name}", method = RequestMethod.GET)
    public String sayHello(@PathVariable String name) {
        return name;
    }


    @RequestMapping(value = "/api/addUser", method = RequestMethod.POST)
    public int addUserInfo(@RequestBody User user) {
        int result = userService.addUser(user);
        return result;
    }

    @RequestMapping(value = "/api/getUser/{id}", method = RequestMethod.GET)
    public User getUserInfo(@PathVariable int id) {
        return userService.findOneUser(id);
    }

    @RequestMapping(value = "/api/registerUser", method = RequestMethod.POST)
    public Object registerUser(@RequestBody User user) {
        return userService.register(user);
    }

}

Service的功能程式碼,程式碼也比較簡單,就是呼叫Repository做一些增刪改查的動作。
package com.dhb.springmvc.service;

import com.dhb.springmvc.entity.User;
import com.dhb.springmvc.repository.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by ${denghb} on 2016/8/2.
 */
@Service
public class UserService {

    @Autowired
    UserDao userDao;

    public int addUser (User user) {
        return userDao.addUser(user);
    }

    public User findOneUser(int id) {
        return userDao.findOneUser(id);
    }

    public String register(User user) {
        User user2 = userDao.findUserByName(user.getName());
        if(user2 == null) {
            userDao.addUser(user);
            return "成功";
        } else {
            return "失敗";
        }
    }
}

下面是repository程式碼:
package com.dhb.springmvc.repository;

import com.dhb.springmvc.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;

/**
 * Created by ${denghb} on 2016/8/2.
 */
@Repository
public class UserDao {
    private DataSource dataSource;
    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public int addUser(User user) {
        String name = user.getName();
        String password = user.getPassword();
        RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);
        int result = jdbcTemplate.update("insert into user(name, password) values(?,?)",name, password);
        return result;
    }

    public User findOneUser(int id) {
        RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);
        User user = jdbcTemplate.queryForObject("select id, name, password from user where id = ?", mapper, id);
        return user;
    }

    public User findUserByName(String name) {
        RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);
        User user = jdbcTemplate.queryForObject("select id, name, password from user where name = ?", mapper, name);
        return user;
    }

}

entity類:
package com.dhb.springmvc.entity;


/**
 * Created by ${denghb} on 2016/8/1.
 */
//@XmlRootElement(name = "demo")
//@XmlRootElement
public class User {
    int id;
    String name;
    String password;

    public User() {

    }

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public User(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    //@XmlElement
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    //@XmlElement
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //@XmlElement
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }


}

增加一個C3P0配置:
package com.dhb.springmvc.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * Created by ${denghb} on 2016/8/2.
 */
@Configuration
//@PropertySource(value = {"classpath:c3p0.properties"})
public class C3P0DataSourceBuilder {

    /**
     * 配置資料來源
     * @return
     */
    @Bean(name = "dataSource")
    public ComboPooledDataSource getDataSource() {
        try {

            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/demo");
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setUser("root");
            dataSource.setPassword("123456");
            dataSource.setMaxPoolSize(75);
            return dataSource;
        } catch (Exception e) {
            return null;
        }
    }
}

先看對應的RestController測試:
package com.dhb.springmvc.controller;

import com.dhb.springmvc.config.DhbWebApplicationInitializer;
import com.dhb.springmvc.entity.User;
import com.dhb.springmvc.service.UserService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.ContextConfiguration;
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 static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * Created by ${denghb} on 2016/8/2.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {DhbWebApplicationInitializer.class})
public class UserControllerTest {
    private MockMvc mockMvc;

    @Mock
    private UserService userService;

    @InjectMocks
    private UserController userController;

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


    @Test
    public void testAdd() {
        int id = 1;
        String name = "鄧海波";
        String password = "123456";

        User user = new User();
        user.setId(id);
        user.setName(name);
        user.setPassword(password);
        when(userService.addUser(user)).thenReturn(1);

        int restUser = userController.addUserInfo(user);
        assertEquals(1, restUser);

        verify(userService).addUser(user);
    }

    @Test
    public void testGetUserInfo() throws Exception {
        int userId = 1;
        String userName = "鄧海波";
        String userPassword = "123456";

        User user = new User();
        user.setId(userId);
        user.setName(userName);
        user.setPassword(userPassword);
        when(userService.findOneUser(userId)).thenReturn(user);

        mockMvc.perform(get("/springmvc/api/getUser/{id}", userId))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("id").value(userId))
                .andExpect(jsonPath("name").value(userName))
                .andExpect(jsonPath("password").value(userPassword));

        verify(userService).findOneUser(userId);
    }

}

首先是Spring的幾個Annotate
RunWith(SpringJUnit4ClassRunner.class): 表示使用Spring Test元件進行單元測試;
WebAppConfiguration: 使用這個Annotate會在跑單元測試的時候真實的啟一個web服務,然後開始呼叫Controller的Rest API,待單元測試跑完之後再將web服務停掉;
ContextConfiguration: 指定Bean的配置檔案資訊,可以有多種方式,這個例子使用的是檔案路徑形式,如果有多個配置檔案,可以將括號中的資訊配置為一個字串陣列來表示;
然後是Mockito的Annotate
Mock: 如果該物件需要mock,則加上此Annotate;
InjectMocks: 使mock物件的使用類可以注入mock物件,在上面這個例子中,mock物件是UserService,使用了UserService的是UserController,所以在Controller加上該Annotate;
Setup方法
MockitoAnnotations.initMocks(this): 將打上Mockito標籤的物件起作用,使得Mock的類被Mock,使用了Mock物件的類自動與Mock物件關聯。
mockMvc: 細心的朋友應該注意到了這個物件,這個物件是Controller單元測試的關鍵,它的初始化也是在setup方法裡面。
Test Case
mockMvc.perform: 發起一個http請求。
post(url): 表示一個post請求,url對應的是Controller中被測方法的Rest url。
param(key, value): 表示一個request parameter,方法引數是key和value。
andDo(print()): 表示打印出request和response的詳細資訊,便於除錯。
andExpect(status().isOk()): 表示期望返回的Response Status是200。
andExpect(content().string(is(expectstring)): 表示期望返回的Response Body內容是期望的字串。
使用print列印處理的資訊類似下面顯示的內容:
MockHttpServletRequest:
         HTTP Method = GET
         Request URI = /springmvc/api/getUser/1
          Parameters = {}
             Headers = {}

             Handler:
                Type = com.dhb.springmvc.controller.UserController
              Method = public com.dhb.springmvc.entity.User com.dhb.springmvc.controller.UserController.getUserInfo(int)

               Async:
   Was async started = false
        Async result = null

  Resolved Exception:
                Type = null

        ModelAndView:
           View name = null
                View = null
               Model = null

            FlashMap:

MockHttpServletResponse:
              Status = 200
       Error message = null
             Headers = {Content-Type=[application/json;charset=UTF-8]}
        Content type = application/json;charset=UTF-8
                Body = {"id":1,"name":"鄧海波","password":"123456"}
       Forwarded URL = null
      Redirected URL = null
             Cookies = []

再來看一下service對應的測試程式碼:
package com.dhb.springmvc.service;

import com.dhb.springmvc.config.DhbWebApplicationInitializer;
import com.dhb.springmvc.entity.User;
import com.dhb.springmvc.repository.UserDao;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * Created by ${denghb} on 2016/8/3.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DhbWebApplicationInitializer.class})
public class UserServiceTest {
    @Mock
    private UserDao userDao;

    @InjectMocks
    private UserService userService;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testRegister() {
        when(userDao.findUserByName(anyString())).thenReturn(null);
        when(userDao.addUser(any(User.class))).thenReturn(0);

        assertThat(userService.register(new User("鄧海波", "567890")), is("成功"));

        verify(userDao).findUserByName(anyString());
        verify(userDao).addUser(any(User.class));
    }
}

Service的單元測試就比較簡單了,大部分內容都在RestController裡面講過,不同的地方就是RestController是使用mockMvc物件來模擬Controller的被測方法,而在Service的單元測試中則是直接呼叫Service的方法。

這是pom.xml檔案

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.dhb.demo</groupId>
  <artifactId>spring4MVCHelloWorldNoXMLDemo</artifactId>
  <packaging>war</packaging>
  <version>0.1.0-SNAPSHOT</version>
  <name>spring4MVCHelloWorldNoXMLDemo Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <jetty.context>/</jetty.context>
    <jetty.http.port>9089</jetty.http.port>
    <jetty.https.port>9444</jetty.https.port>
    <jetty.stopPort>10081</jetty.stopPort>

    <spring.version>4.1.4.RELEASE</spring.version>
  </properties>

  <dependencies>
    <!-- spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>


    <!-- junit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <!-- jackson -->
    <dependency>
      <groupId>org.codehaus.jackson</groupId>
      <artifactId>jackson-mapper-asl</artifactId>
      <version>1.9.13</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.3.4</version>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.1</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!-- mockito -->
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>1.10.19</version>
      <scope>test</scope>
    </dependency>

    <!-- jsonPath -->
    <dependency>
      <groupId>com.jayway.jsonpath</groupId>
      <artifactId>json-path</artifactId>
      <version>2.2.0</version>
    </dependency>
    <dependency>
      <groupId>com.jayway.jsonpath</groupId>
      <artifactId>json-path-assert</artifactId>
      <version>2.2.0</version>
    </dependency>

    <!--c3po-->
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>

  </dependencies>



  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
          <showWarnings>true</showWarnings>
        </configuration>
      </plugin>

      <!--maven jetty 外掛配置-->
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.2.17.v20160517</version>
        <configuration>
          <webApp>
            <contextPath>${jetty.context}</contextPath>
          </webApp>
          <httpConnector>
            <port>${jetty.http.port}</port>
          </httpConnector>
          <stopKey>jetty</stopKey>
          <stopPort>${jetty.stopPort}</stopPort>
          <!--<scanIntervalSeconds>2</scanIntervalSeconds>-->
        </configuration>
      </plugin>
    </plugins>
    <finalName>Spring4MVCHelloWorldNoXMLDemo</finalName>
  </build>
</project>


這裡介紹省略了一些spring web環境的配置,可以去我前面幾篇部落格去看哈~

相關推薦

基於Spring MVC(REST API)單元測試mockito

最近在公司用的Spring Mvc REST API框架做了一個專案,並且做了基於Spring的單元測試,今天先講一下基於Spring框架的單元測試,測試使用的是Spring自帶的test元件,再結合Mockito一起編寫測試案例,以下示例會包括Controller和Ser

聊聊單元測試——Spring Test+JUnit完美組合

進行 popu transacti csdn 基於 lease 2.4 獲取bean strong 本著“不寫單元測試的程序員不是好程序員”原則,我在堅持寫著單元測試,不敢說所有的Java web應用都基於Spring,但至少

Android單元測試6:使用dagger2來依賴注入

注: 1. 程式碼中的 //<= 表示新加的、修改的等需要重點關注的程式碼 2. Class#method表示一個類的instance method,比如 LoginPresenter#login 表示 LoginPresenter的login(非靜態)方法。 問題

spring-test單元測試-進行struts action的請求單元測試

單元測試雖不強求,但你一旦養成習慣,你會愛上它。另外我們保證程式碼質量的兩個很重要的手段,一個是頭(單元測試)一個是尾(codereview)。那麼我們最常用的單元測試就是通過junit來進行,spring-test框架很好的集成了junit來進行這項工作,比如測試dao,

Juint 單元測試1

cti path 語言 html size add 版本號 icon build Junit 是一個基於Java語言的回歸單元測試框架。是白盒測試的一種技術,記住這些就可以了。 為項目添加Junit 1 右鍵項目名稱選擇“Properties”,在彈

Spring MVC http請求地址映射

問題 custom 支持 ref quest path pin 風格 依據 Spring [email protected]/* */@RequestMapping的方法進行映射,然後調用映射的方法處理請求,這個分發過程默認是由DispaterServlet處理

vue2.0單元測試

.com str images alt 需求 org 封裝 min 測試 1.在vue init webpack XXX創建項目的時候 最後2步選擇YES就啟動了vue單元測試開始了 2.測試是使用karma+mocha框架來實現的方法,安裝虛擬瀏覽器模塊Phanto

單元測試

pan 如何 而且 rtt mage 圖片 父類 agent char 前言:前面講了java的junit,這裏講一下spring+junit。轉載請註明出處:https://www.cnblogs.com/yuxiaole/p/9419224.html Spring

單元測試

features 連接 相關 ext add 才會 ofo itself 源碼 前言:junit 介紹。轉載請註明出處:https://www.cnblogs.com/yuxiaole/p/9405519.html 二、 java單測工具 junit4介紹 Java用的

Spring MVC 配置及應用 註解配置2

SpringMVC 註解應用 /login.do 具體的步驟 -->DispatcherServlet -->HandlerMapping -->LoginController  -->ViewReslover -->login.jsp (1)&

如何對第一個Vue.js元件進行單元測試

  首先,為什麼要單元測試元件?   單元測試是持續整合的關鍵。通過專注於小的、獨立的實體,確保單元測試始終按預期執行,使程式碼更加可靠,你可以放心地迭代你的專案而不必擔壞事兒。   單元測試不僅限於指令碼。可以獨立測試的任何東西都是可單元測試的,只要你遵循一些好的做法。這些例項包括單一責任、可預測性和鬆

python用selenium自動化測試

python 用selenium做自動化測試: 在w3school裡搜尋css python3 test_w3school_search.py XPath路徑表示式: http://www.ruanyifeng.com/blog/2009/07/xpath_pa

如何對第一個Vue.js元件進行單元測試

我們的首次測試 讓我們來寫首個測試。我們首先需要使用shallowMount手動掛載我們的元件,並將其儲存在我們將執行斷言的變數中。我們還可以通過propsData屬性傳遞道具作為物件。 已安裝的元件是一個物件,它有一些實用方法: 然後,我們可以寫第一個斷言: 讓我們來

如何對第一個Vue.js組件進行單元測試

發生 清理 定位元素 after 斷言 www. array 12c 良好的 我們的首次測試 讓我們來寫首個測試。我們首先需要使用shallowMount手動掛載我們的組件,並將其存儲在我們將執行斷言的變量中。我們還可以通過propsData屬性傳遞道具作為對象。

深入淺出Android單元測試單元測試基礎

想學習單元測試無從下手,本文對以最易懂的方式介紹單元測試。 若有錯漏,煩請斧正。轉載請註明出處。歡迎關注程式引力 作者:程式引力 | 謝一 (Evan Xie) 郵箱:[email protected] 軟體測試作為軟體質量的保障,有著十分重要的意義。按照不同

C# 單元測試入門

注:本文示例環境 VS2017XUnit 2.2.0 單元測試框架xunit.runner.visualstudio 2.2.0 測試執行工具Moq 4.7.10 模擬框架 什麼是單元測試? 確保軟體應用程式按作者的期望執行操作,其中最好的一種方法是擁有自動化測試套件。 可以對軟體應用程式進行

<VS2017> 編寫VC++單元測試 -新建單元測試工程

pre 菜單 運行 soft 官方 turn sof 自動 平臺 開發人員自己編寫單元測試是一個非常好的習慣。單元測試不但能夠驗證自己所編寫的代碼是否存在問題,避免提交給測試人員時才發現bug,也可以為將來改動代碼的人提供驗證代碼功能正確性的途徑。在我有限的工作生涯中並未寫

LCN基於Spring cloud2.0實現分散式事物管理LCN的修改和部署

首先,對專案進行編譯,裝好maven環境,jdk環境。命令如下,注意這裡需要jdk1.8以上 mvn install -Dmaven.test.skip=true 打包之後,將編譯好的jar包上傳到自己的私服。 獲取最新的tx-manager的jar

Python用Selenium自動化測試:Page Object專題

Page Object專題 頁面物件模型的設計優勢: 創造可以被多個測試用例共享的可重用程式碼。 減少大量重複的程式碼。 如果使用者介面發生改變,則修改只用在一處進行。 圖片來源: 目的:測試程式碼與被測頁面物件程式碼分離,後期如果有頁面元素髮生了更改,

AndroidStudio androidTest安卓測試 test單元測試Junit

 androidTest是整合測試,可以執行在裝置或虛擬裝置上,需要編譯打包為APK在裝置上執行,可以實時檢視細節 test 是單元測試,執行在本地開發機上,可以脫離Android執行環境,速度快 Android Studio 測試分 androidTest 安卓測試(下圖綠箭頭)