使用spring测试模块搭建自动测试平台

老大给了这么个任务,搭建一个自动测试平台,他希望的效果是,在改动代码之后,可以一键进行测试,想想看,这个东西是很有意义的,总不能改动一点代码,就要从页面上重新走遍回归测试吧?太傻了。于是调研了一些Spring的测试模块。
Spring的测试模块主要存放在org.springframework.test包下,它包括了集成测试和单元测试,这里我只处理了集成测试,原因有几点:

  1. 处理起来简单,在构造HTTP请求的时候都是构造字符串,不会存在构造对象的问题(但是最后还是遇到了,在设置session的过程中)
  2. 可以走完所有流程,从前端的filter、servlet一直到最后响应输出都可以测试到。
  3. Spring集成了强大的Mock对象,包括了MockHttpRequest\Response\Session等等,并且在request中还实现了诸如设置字符集、设置content内容,构造URL等等等。

详细的信息,推荐一个博客这里写链接内容

主体类

框架的核心是通过解析XML文件,并利用反射机制构造Mock请求。主体类非常简单:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration(value = "src/com/dacas")
@ContextConfiguration("/applicationContext.xml")
@Transactional
public class TestHandler {
    List<TestClass> classes;
    @Autowired
    private WebApplicationContext wac;
    private MockMvc mockMvc;
    @Before
    public void init(){
        ConfigParser configParser = new ConfigParser();
        classes = configParser.getTestClass();//读取XML文件中的数据
        //初始化MockMVC上下文环境
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }
    @Test
    public void doTest(){
        try {
            for(TestClass tmpClass:classes){
                redirectPath(tmpClass);//重定向到某输出文件中
                List<TestUrl> urls = tmpClass.getUrls();
                for(TestUrl url:urls){
                    String urlString = url.getCompositeUrl();//构造URL请求
                    MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(urlString);//构造requestBuilder
                    initRequestParams(requestBuilder,url);//初始化请求参数,包括content、parameter、session等
                    mockMvc.perform(requestBuilder).andDo(MockMvcResultHandlers.print());//真正实施测试,但是这里面并没有添加过多的断言,比如andExpect、或者assert等,这是根据需求定的。
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * initial requestbuilder params
     * @param builder
     * @param url
     */
    private void initRequestParams(MockHttpServletRequestBuilder builder,TestUrl url) {
        //设置编码格式
        builder.characterEncoding("utf-8");
        TestParameter parameter = url.getParamter();
        TestContent content = url.getContent();
        TestSession sessionData = url.getSession();

        //添加session
        if(sessionData != null){
            MockHttpSession mockHttpSession = new MockHttpSession();
            Map<String, Object> maps = sessionData.getSession();
            Set<Map.Entry<String, Object>> sets = maps.entrySet();

            for(Map.Entry<String, Object> entry:sets){
                mockHttpSession.setAttribute(entry.getKey(), entry.getValue());
            }
            builder.session(mockHttpSession);
        }
        //添加参数,构造form表单数据
        if(parameter != null){
            Map<String, String> paramMaps = parameter.getParams();
            Set<Map.Entry<String, String>> sets = paramMaps.entrySet();

            for(Map.Entry<String, String> entry:sets){
                builder.param(entry.getKey(), entry.getValue());//这里为什么用params而不是content,请见上篇博客:http://blog.csdn.net/zdavb/article/details/50999405
            }
            return;
        }
        //获取content数据
        if(content != null){
            String contentData = content.getContent();
            JSONObject jsonObjData = content.getObject();
            JSONArray jsonArrayData = content.getArray();

            if(contentData.length() != 0)
                builder.content(contentData);
            else if(jsonObjData != null && jsonObjData.length() != 0)
                builder.content(jsonObjData.toString());
            else if(jsonArrayData != null && jsonArrayData.length() != 0)
                builder.content(jsonArrayData.toString());
        }
    }

    /**
     * 查看是否需要将输出重定向,将输出结果输出到文件中
     * @param config
     * @throws IOException 
     */
    private void redirectPath(TestClass config) throws IOException{
        String path = config.getRedirectPath();
        if(path.equals(""))//默认,不需要重定向
            return;
        PrintStream stream = new PrintStream(path);
        System.setOut(stream);
    }
}

解析XML文件

解析配置文件是工作量较大的地方,首先读取框架的配置文件,框架的配置文件还比较简单:

<?xml version="1.0" encoding="UTF-8"?>
<config>
  <!--是否将IO重定向-->
  <IORedirect type="true" path="./out.txt"/>
  <!--读取待测试的类的配置文件-->
  <!--允许使用通配符*,会根据mapping中的顺序依次进行测试-->
  <mapping paths="com/dacas/test/*.test.xml"></mapping>
</config>

处理XML文件的主要类

public class ConfigParser {
    private String mainPath = "/DCSTest.config.xml";
    /**
     * 读取主配置问题classpath路径下的DCSTest.config.xml文件
     */
    public List<TestClass> getTestClass(){
        InputStream inputStream = getClass().getResourceAsStream(mainPath);
        MainConfigParser parser = new MainConfigParser();

        List<TestClass> testClasses = new LinkedList<TestClass>();
        try {
            MainConfig mainConfig = parser.getMainConfigs(inputStream);
            redirectPath(mainConfig);
            //加载子配置文件
            List<InputStream> inputStreams = mainConfig.getInputStreams();
            SubTestClassParser subConfig = new SubTestClassParser();

            for(InputStream input:inputStreams){
                TestClass testClass = subConfig.getTestClass(input);
                testClasses.add(testClass);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return testClasses;
    }
    /**
     * 查看是否需要将输出重定向,将输出结果输出到文件中
     * @param config
     * @throws IOException 
     */
    private void redirectPath(MainConfig config) throws IOException{
        String path = config.getRedirectPath();
        if(path.equals(""))//默认,不需要重定向
            return;
        PrintStream stream = new PrintStream(path);
        System.setOut(stream);
    }
}

待测试控制器配置文件

读取完主配置文件后,会根据主配置文件中的mapping项读取相应的子配置文件,虽然没有限制,但是还是建议,将一个控制器类中处理的URL请求放在一个文件中,这样方便修改。

<?xml version="1.0" encoding="UTF-8"?>
<!--是否重定向,当不为""时将会重定向到相应文件-->
<test redirect="">
    <!--name为待测试URL-->
    <url name="/get_card_info.do">
        <!-- URL模板,在URL中进行参数拼接 -->
        <template>
            <init name="abcd" value="dfjksajdfklas"/>
            <init name="eqaz" value="sdfasdfsadf"/>
        </template>
        <!-- HTTP form表单格式进行参数发送 -->
        <parameter>
            <init name="popli" value="asdfasdfjk" />
            <init name="qwert" value="qwerty"/>
        </parameter>
        <!-- HTTP body中JSONArray、JSONObject -->
         <content>
         <!--array中包含的为JSONArray中一个JSONObject对象-->
         <!--当没有array标签时,表示一个jsonobject对象-->
            <array>
                <init name="name" value="a" />
                <init name="age" value="18" />
            </array>
            <array>
                <init name="name" value="b" />
                <init name="age" value="18" />
            </array>
        </content>

        <!-- 定义HTTP session,name为session中key,value为实体类,类中必须由一个使用testcase作为注解的静态类来返回一个测试对象,为了减少对该类的污染,建议将该方法定义为private -->
        <session>
            <init name="bound" value="com.dacas.testcase.Bound" />
        </session>
    </url>
</test>

然后剩下的过程就是解析了,解析完成之后,通过在TestHandler中运行Run as JUnit进行测试。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值