程序员阿里云

Spring集成TestNG测试MVC Controller

2018-11-06  本文已影响11人  良辰美景TT

  在项目中需要写单元测试,如何保证写的单元测试的质量是比较高的。有以下几个原则。

Spring集成TestNG

        <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

上一篇 下一篇

猜你喜欢

热点阅读