Spring集成TestNG測試MVC Controller

  在項目中需要寫單元測試,如何保證寫的單元測試的質量是比較高的。有以下幾個原則。

  • 編寫具有確定性結果的測試用例。
  • 代碼中使用斷言,而不是System.out.print語句輸出結果,然後人工驗證。
  • 對於需要訪問數據庫的操作或者外部數據,可以使用內存數據庫或者EasyMock之類的工具。
  • 測試完數據之後,儘可能的恢復現場(測試之前的環境,這樣測試用例便可以重複執行)。

Spring集成TestNG

  • 首先把需要的jar包加入到項目裏,因爲都是測試相關的,所以scope都是test,引入jar包的pom.xml需要增加如下的依賴(spring 的版本需要在3.2以上):
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
<!-- 用於記錄jdbc的日誌, 輸出的日誌格式會帶上相應的參數-->
        <dependency>
            <groupId>com.googlecode.log4jdbc</groupId>
            <artifactId>log4jdbc</artifactId>
            <version>1.2</version>
            <scope>test</scope>
        </dependency>
<!-- H2內存數據庫, 適合用於處理測試用例的執行-->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.197</version>
            <scope>test</scope>
        </dependency>
  • 編寫相應的測試用例。代碼如下:
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

//@WebAppConfiguration:測試環境使用,用來表示測試環境使用的ApplicationContext將是WebApplicationContext類型的;value指定web應用的根;
@WebAppConfiguration()
//@ContextHierarchy:指定容器層次
@ContextHierarchy({
                    @ContextConfiguration(locations = {
                                                        "classpath:applicationContext.xml" //這裏的applicationContext.xml文件,如果有特殊的bean需要配置,則需要放在src/test/resources目錄下
                    }),
                    @ContextConfiguration({
                                            "classpath:spring-mvc.xml"
                    })
})
public class SysUserControllerTest extends AbstractTestNGSpringContextTests {

    //注入web環境的ApplicationContext容器
    @Autowired
    private WebApplicationContext wac;
    
  
    private MockMvc               mockMvc;

    //這裏可以執行初使化的數據腳本, 如果沒有,也可以不執行這個方法
    SysUserControllerTest() {
        executeSql("sql/mysql/schema.sql");
        executeSql("sql/mysql/import-data.sql");
    }

    //BeforeClass會在testcase執行之前執行
    @BeforeClass
    public void setUp() {
       //MockMvcBuilders.webAppContextSetup(wac).build()創建一個MockMvc進行測試
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    private void executeSql(String sqlPath) {
        DataSource dataSource = new DataSource();
        dataSource.setDriverClassName("net.sf.log4jdbc.DriverSpy"); //採用這個driver 可以方便記錄jdbc的日誌
        dataSource.setUrl("jdbc:log4jdbc:h2:mem:test;MODE=MySql;DB_CLOSE_DELAY=-1");//H2數據訪問的URL
        dataSource.setUsername("sa");
        dataSource.setPassword("");

        Connection connection = null;
        Statement st = null;
        try {
            connection = dataSource.getConnection();
            // Thread.currentThread().getContextClassLoader().getResource(sqlPath) 得到的是以file:/開頭的路徑, 所以需要截取後6位的字符
            String path = Thread.currentThread().getContextClassLoader().getResource(sqlPath).toString().substring(6);
            st = connection.createStatement();
            st.execute("runscript from '" + path + "'");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }

            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Test
    public void testadd() throws Exception {
        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/sysUser/add.do?username=aaa&name=aaaa&password=aaaaaa&domainUsername=aaaa")). //perform用於執行一個請求
                andDo(MockMvcResultHandlers.print()).  //增加一個結果處理器
                andExpect(MockMvcResultMatchers.status().isOk()). //執行完成後的斷言
                andReturn(); //執行完成後返回相應的結果
        String content = result.getResponse().getContentAsString();
        JSONObject jsonObject = JSON.parseObject(content);
        //採用Asser的方式進行斷言
        Assert.assertEquals(jsonObject.get("code"), "200");
    }

}

上面的代碼需要關注的點有下面幾個: 1: 如果spring的配置文件裏有bean的構造方式跟線上的不一致,需要在src/main/resources目錄下新建spring的配置文件,這樣testcase執行的時候加載的是測試環境的文件。比如數據庫的datasource bean就有可能不一樣。 2:在spring IOC容器之前如果有數據庫需要進行初使化的話,則可以在這個測試類的構造方法裏執行相應的代碼。 3:如果需要在spring IOC容器初使化之後執行相應的數據庫初使代碼,則可以在testng的@BeforeClass方法裏執行。 4:在測試具體的接口的時候,需要用斷言對結果進行預測。而不是打印相應的信息。 5:實際項目中可以參考使用H2內存數據庫,這樣寫的sql有什麼問題,測試用例也能夠儘快發現。 6:這樣寫的測試類會連同Spring MVC的基礎設施(如DispatcherServlet調度、類型轉換、數據綁定、攔截器, 最終渲染的視圖 @ResponseBody生成的JSON/XML、JSP、Velocity等)但是不會測試web.xml裏配置的filter

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章