单元测试---自动化测试查询结果集
来源:互联网 发布:自动阅读的软件 编辑:程序博客网 时间:2024/04/29 04:32
实际上我把自动化单元测试分为了两种
- 针对增删改操作的单元测试
- 针对查询的单元测试
其中“针对增删改操作的单元测试”,可以用dbunit和springtestdbunit来编写单元测试,而“针对查询的单元测试”,我孤陋寡闻没有找到什么现成的工具去解决(哪位朋友知道有这样的工具可以指点一下,谢啦)。下面会一步一步的讲述我自行开发的工具包,解决“针对查询的单元测试”问题。
首先还是从dbunit和springtestdbunit说起
dbunit流程大概是这样的:
- 单元测试前重置数据库
- 单元测试后比对数据库结果与预期结果是否一致
springtestdbunit使用了注解配置,一个springtestdbunit的单元测试大致是这样的
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = {"classpath:com/sztb/dp/test/testContext.xml"})@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class})@DbUnitConfiguration(dataSetLoader= XmlDataSetLoader.class)public class UserTest { @Test @DatabaseSetup("classpath:com/look/test/xml/InputUserDelete.xml") @ExpectedDatabase(assertionMode = DatabaseAssertionMode.NON_STRICT, value = "classpath:com/look/test/xml/ResultUserDelete.xml") public void testUserDelete() throws Exception { //dbunit test code UserService userService= new UserService(); userService.deleteUser(1L); }}
该代码对删除用户操作进行了单元测试,@DatabaseSetup设置了数据库的数据集,@ExpectedDatabase设置了期望的数据集,单元测试代码执行完毕后,程序会检查数据库最终结果,与期望数据集的一致性,完成断言。
但是如果要测试查询操作,数据库在单元测试前后是无变化的,dbunit也就不适用了,我们需要对查询到的结果集的属性进行断言,秉承dbunit的思路,我们可以设想用如下的方式进行基于查询的单元测试的编写,也就是说,我们开发这个工具包,要达到的效果就是下面这样,通过配置我们自己的监听器和自定义的注解,对查询到的结果进行与xml文件的比对
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = {"classpath:com/sztb/dp/test/testContext.xml"})@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class, ProtoResultTestExecutionListener.class})@DbUnitConfiguration(dataSetLoader= XmlDataSetLoader.class)public class UserTest { @Test @DatabaseSetup("classpath:com/look/test/xml/InputGetUsers.xml") @ExpectedProtoResult("classpath:com/look/test/protoResultXml/ResultGetUsers.xml") public void testGetUsers() throws Exception { //dbunit test code UserService userService= new UserService(); User.GetUserResponse resp = userService.getUsers();// 将结果集设置到protoResult容器 ResultContainer.getInstance().setResultSet(resp); }}
有两点需要注意的地方
- @ExpectedProtoResult注解,设置预期结果集
- @TestExecutionListeners的设置中,另外添加了一个监听器ProtoResultTestExecutionListener.class,这是自行编写的监听器,用来在单元测试执行完毕后,处理@ExpectedProtoResult注解,监听器ProtoResultTestExecutionListener监听我们的单元测试,运行我们的自定义代码,自定义代码中需要解析@ExpectedProtoResult注解中配置的预期结果集,并与ResultContainer中我们的查询结果进行一致性断言。
下面逐一介绍我都编写了哪些类
1.首先,我们可能需要一个这样的工具类,能将得到的结果对象转为符合一定规则的xml格式,并且有良好格式化方便阅读和修改。(本文中的bean对象为protoc生成的javabean)
public class XMLWriter { public static void writeXML(MessageLite protoObject , String dest) throws Exception { String xml = Proto2XML.toXml(protoObject,true); BufferedWriter bufferedWriter = null; FileWriter fileWriter = null; try { fileWriter = new FileWriter(dest); bufferedWriter = new BufferedWriter(fileWriter); bufferedWriter.write(xml); bufferedWriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { fileWriter.close(); bufferedWriter.close(); } catch (IOException e) { e.printStackTrace(); } } }}
2.工具类Proto2XML .java
由于这个类涉及到proto javabean到xml的转换,有一些局限性,该处代码省略,思路就是将javaBean转为json格式,再转为带换行和缩进的的xml格式。
public class Proto2XML { public static org.json.JSONObject proto2JSONObject(MessageLite protoObject) throws org.json.JSONException { org.json.JSONObject jsonObject = new org.json.JSONObject(); /******* proto javabean cast to json ********/ return jsonObject; } public static String toXml(Object protoObject) throws JSONException { JSONObject jsonObject = proto2JSONObject(protoObject); return XML.toString(jsonObject,Path.XMLROOT); } public static String toXml(Object protoObject,boolean format) throws JSONException { String xml = toXml(protoObject); if(format) { xml = formatXML(xml); } return xml; }
生成的xml文件形如
<?xml version="1.0" encoding="utf8"?><dataset> <errCode type="string">ERR_OK</errCode> <errMsg type="string">ERR_OK</errMsg> <users class="array"> <e class="object"> <id type="string">1</id> <username type="string">jack</username> <age type="string">20</age> </e> <e class="object"> <id type="string">2</id> <username type="string">jim</username> <age type="string">22</age> </e> </users></dataset>
说一点题外话,上面代码中的MessageLite是protobuf中的一种消息类型,通过对proto文件optimize_for选项的设置,可以选择消息类型为MessageLite或Message,使用MessageLite的效率高一点,但是牺牲了Message的反射功能,比如说,Message可以直接使用getAllfields这样的方法,也可以取到Message的name,而MessageLite就不行,因为MessageLite不提供这样的方法,所以基于Message的protobuf进行xml转换的时候,有现成的框架可以完成Message到xml的转换(因为框架可以直接调用Message原生getAllFields、getName等方法),例如protobuf-java-format。而我们项目中使用的是MessageLite,它转xml的部分,只能由我们自己开发,并没有现成的框架。
我选择了proto javabean先转json再转xml的方式,proto javabean转json用到的json包,我选择了org.json 因为我后续会用到JsonAssert框架来进行json一致性的断言,而JsonAssert使用的就是org.json。json转xml用到的json包,我选择了net.sf.json包,因为org.json包下的xml转换有两个缺点,第一,转换为xml后,JSONArray结构不清晰,如果JSONArray中只有一条JSON,xml转换成JSON的时候,这个JSONAraay也会转换为JSONObject。
第二,对数值的处理有些问题,比如xml中有一个属性值为4.0,转为json的时候4.0会变为4,自动转为了整数,可以说是一种失真,不利于单元测试。
net.sf.json的xml转换功能就不存在这两种问题。
详细了解protobuf及其选项(Options)可参考下面链接
http://www.cnblogs.com/dkblog/archive/2012/03/27/2419010.html
3.自定义监听器ProtoResultTestExecutionListener.java
public class ProtoResultTestExecutionListener extends org.springframework.test.context.support.AbstractTestExecutionListener { @Override public void afterTestMethod(TestContext testContext) throws org.json.JSONException, IOException { Method method = testContext.getTestMethod(); if(method.isAnnotationPresent(ExpectedProtoResult.class)) { ExpectedProtoResult protoResult = method.getAnnotation(ExpectedProtoResult.class); String dataSetLocation = protoResult.value(); assertNotNull(dataSetLocation); if (StringUtils.hasLength(dataSetLocation)) { String xml = XMLLoader.load(testContext.getClass(),dataSetLocation); JSONUtil.compareXmlAndProto(xml, ResultContainer.getInstance().getResultSet()); } } }}
4.JSONUtil.java
public class JSONUtil { public static void compareXmlAndProto(String xml , MessageLite protoObject) throws JSONException { org.json.JSONObject json1 = string2JSON(xml2JSONString(xml)); org.json.JSONObject json2 = Proto2XML.proto2JSONObject(protoObject); System.out.println("Expected Result : "+json1.toString()); System.out.println("True Result : " + json2.toString()); JSONAssert.assertEquals(json1, json2, true); } public static String xml2JSONString(String xml){ XMLSerializer xmlSerializer = new XMLSerializer(); xmlSerializer.setRootName(Path.XMLROOT); return xmlSerializer.read(xml).toString(); } public static String jsonString2XML(String json){ net.sf.json.JSONObject netJson = net.sf.json.JSONObject.fromObject(json); XMLSerializer xmlSerializer = new XMLSerializer(); xmlSerializer.setRootName(Path.XMLROOT); return xmlSerializer.write(netJson,"utf8"); } public static org.json.JSONObject string2JSON(String json) throws JSONException, JSONException { return new org.json.JSONObject(json); }}
5.自定义注解@ExpectedProtoResult
@java.lang.annotation.Documented@java.lang.annotation.Inherited@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD})public @interface ExpectedProtoResult { java.lang.String value();}
6.protoXML的解析器
这个解析器用到了spring的core包,解析路径时,与spring使用相同的方式
public class XMLLoader{ protected static ResourceLoader getResourceLoader(Class<?> testClass) { return new ClassRelativeResourceLoader(testClass); } protected static String[] getResourceLocations(String location) { return new String[] { location }; } public static String load(Class<?> testClass , String location) throws IOException { StringBuffer sb = new StringBuffer(); ResourceLoader resourceLoader = getResourceLoader(testClass); String[] resourceLocations = getResourceLocations(location); for (String resourceLocation : resourceLocations) { Resource resource = resourceLoader.getResource(resourceLocation); if (resource.exists()) { sb.append(getProtoXML(resource.getInputStream())); }else { throw new IOException(resource.getURI().getPath()); } } return sb.toString(); } private static String getProtoXML(InputStream is) throws IOException { return inputStream2String(is); } private static String inputStream2String(InputStream is) throws IOException { int len = 0; StringBuffer str=new StringBuffer(""); try { InputStreamReader isr = new InputStreamReader(is); BufferedReader in = new BufferedReader(isr); String line = null; while( (line=in.readLine())!=null ) { str.append(line); len++; } in.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } return str.toString(); }}
7.装载返回结果对象的ResultContainer
public class ResultContainer { private static ResultContainer resultContainer = new ResultContainer(); private ResultContainer(){} public static ResultContainer getInstance() { return resultContainer; } private MessageLite resultSet = null; public MessageLite getResultSet() { return resultSet; } public void setResultSet(MessageLite resultSet) { this.resultSet = resultSet; }}
可以看到,使用方式与springtestdbunit基本一致,只是多了ResultContainer.getInstance().setResultSet(resp);这样一个步骤,
那是因为springtestdbunit在@ExpectedDatabase的时候,比对的是数据库的结果,使用@ContextConfiguration中配置的数据库就可以,
而@ExpectedProtoResult比对的不是执行完单元测试后数据库的情况,而是执行完单元测试后,比对我们得到的结果对象,所以我们需要自行设置一下比对的对象resp。
- 单元测试---自动化测试查询结果集
- Android自动化测试生成单元测试结果报告
- Android自动化测试生成单元测试结果报告
- Android自动化测试生成单元测试结果报告
- Android自动化测试生成单元测试结果报告
- Android使用Robotium自动化测试junit生成单元测试结果报告
- 单元测试和测试自动化
- 自动化测试 - 单元测试
- python单元测试(自动化测试)
- 单元测试/自动化测试
- 敏捷自动化测试 (黑盒单元测试)
- 单元测试框架进行自动化测试
- Jasmine, 自动化测试, 单元测试 --- 使用
- 单元测试框架进行自动化测试
- android自动化测试之单元测试实例
- android自动化测试之单元测试实例 .
- 安卓自动化测试及单元测试
- angular单元测试与自动化UI测试实践
- 关于Azure存储账户中存储虚拟机VHD文件的注意事项
- Android检测版本更新 实现逻辑,UI效果自己处理
- linux下为php安装pdo_mysql扩展
- 从尾到头打印链表
- 链表实现多项式的加法和乘法
- 单元测试---自动化测试查询结果集
- ORACLE-基础十七(Oracle Net Server Dide Configuration)
- Android 自定义按钮
- ios判断邮箱,手机号码,车牌号是否合法(正则表达)
- HBase RowKey的设计原则
- IP 分片丢失重传
- UITableView每个cell之间的默认分割线怎么去掉,cell的显示
- smarty模板引擎对各种数据的基本操作
- House Robber