自动化测试中,维护测试数据 是一项很重要的工作,为了达到用例和测试数据的分离,我们一般会把数据存储在外部文件 或 数据库,为了简化测试,测试用例编写人员想只需要维护测试数据文件,在用例里通过简单声明,即可随意读取本次测试用例所需要的测试数据,我的思路还是为测试用例自动注入测试数据对象。
代码如下:
定义测试对象标签,只要在用例里的类域加了此标签 测试框架便认为此域变量是个测试数据对象:
package ec.qa.autotest.ui.custom.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author xin.wang
* 自动装配标签
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataObject {
public String DataNode() default "";
}
定义固定结构的测试数据文件 ,我使用的是XML:
<?xml version="1.0" encoding="UTF-8"?>
<testdata>
<data-node id="AdminPortalLoginPage">
<data key="UserName" value="admin"/>
<data key="PassWord" value="111111"/>
</data-node>
<data-node id="AddProductLibraryPage">
<data key="ProLibName" value="测试产品库"/>
<data key="PLDescription" value="测试专用"/>
</data-node>
</testdata>
package ec.qa.autotest.ui.admin.portal.testcases;
import java.util.HashMap;
import org.testng.annotations.Test;
import ec.qa.autotest.ui.admin.portal.pageobject.AddProductLibraryPage;
import ec.qa.autotest.ui.admin.portal.pageobject.NavigationMenu;
import ec.qa.autotest.ui.admin.portal.pageobject.ProductLibraryPage;
import ec.qa.autotest.ui.common.action.AdminLoadingProgress;
import ec.qa.autotest.ui.custom.annotation.AutoInject;
import ec.qa.autotest.ui.custom.annotation.DataObject;
import ec.qa.autotest.ui.testbase.AdminPortalTestBase;
public class AddProductLibraryProtal extends AdminPortalTestBase {
@DataObject(DataNode="AddProductLibraryPage")
private HashMap<String,String> dataMap;
@Test(invocationCount = 100)
public void addProductLibrary() throws Exception {
}
}
数据对象标签 有个参数 :DataNode 给这个参数赋值你在这个测试用例需要的数据块,对应的参数值是数据文件的 data-node 的 id 如上面用例使用的使用数据文件这部分值:
<data-node id="AdminPortalLoginPage">
<data key="UserName" value="admin"/>
<data key="PassWord" value="111111"/>
</data-node>
支持同时使用多个数据块的数据:
package ec.qa.autotest.ui.admin.portal.testcases;
import java.util.HashMap;
import org.testng.annotations.Test;
import ec.qa.autotest.ui.admin.portal.pageobject.AddProductLibraryPage;
import ec.qa.autotest.ui.admin.portal.pageobject.NavigationMenu;
import ec.qa.autotest.ui.admin.portal.pageobject.ProductLibraryPage;
import ec.qa.autotest.ui.common.action.AdminLoadingProgress;
import ec.qa.autotest.ui.custom.annotation.AutoInject;
import ec.qa.autotest.ui.custom.annotation.DataObject;
import ec.qa.autotest.ui.testbase.AdminPortalTestBase;
public class AddProductLibraryProtal extends AdminPortalTestBase {
@DataObject(DataNode="AddProductLibraryPage,AdminPortalLoginPage")
private HashMap<String,String> dataMap;
@Test(invocationCount = 100)
public void addProductLibrary() throws Exception {
}
}
只需要在给DataNode 赋值时用逗号隔开 测试用例完整代码:
package ec.qa.autotest.ui.admin.portal.testcases;
import java.util.HashMap;
import org.testng.annotations.Test;
import ec.qa.autotest.ui.admin.portal.pageobject.AddProductLibraryPage;
import ec.qa.autotest.ui.admin.portal.pageobject.NavigationMenu;
import ec.qa.autotest.ui.admin.portal.pageobject.ProductLibraryPage;
import ec.qa.autotest.ui.common.action.AdminLoadingProgress;
import ec.qa.autotest.ui.custom.annotation.AutoInject;
import ec.qa.autotest.ui.custom.annotation.DataObject;
import ec.qa.autotest.ui.testbase.AdminPortalTestBase;
public class AddProductLibraryProtal extends AdminPortalTestBase {
@AutoInject
private NavigationMenu menu;
@AutoInject
private ProductLibraryPage porLibrary;
@AutoInject
private AddProductLibraryPage addProLibrary;
@AutoInject
private AdminLoadingProgress adminCommon;
@DataObject(DataNode="AddProductLibraryPage,AdminPortalLoginPage")
private HashMap<String,String> dataMap;
@Test(invocationCount = 100)
public void addProductLibrary() throws Exception {
System.out.println(dataMap.get("UserName"));
menu.goToProdcutLibPage();
porLibrary.addButtonOnclick();
adminCommon.wiatForLoadingTOComplete();
addProLibrary.AddProduvtLibraryPortal(dataMap.get("ProLibName"),dataMap.get("PLDescription"));
porLibrary.waitForExpectValue(dataMap.get("ProLibName"));
}
}
运行结果:
======测试用例: addProductLibrary 开始执行======
===测试用例运行的浏览器类型:Chrome ===
测试网站地址:
Starting ChromeDriver 2.20.353145 (343b531d31eeb933ec778dbcf7081628a1396067) on port 14786
Only local connections are allowed.
admin
==页面加载中....==
==页面加载已完成!==
=====测试用例: addProductLibrary 运行成功=====
killed the useless process
PASSED: addProductLibrary
===============================================
Default test
Tests run: 1, Failures: 0, Skips: 0
===============================================
===============================================
Default suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================
上面自动注入数据对象的实现过程:
定义数据文件格式后,实现XML文件解析程序(DOM4j):
package ec.qa.autotest.ui.utility;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class ParseDataXml {
private static String testDataFielsuffix = ".xml";
private static String dataNodeTag = "data-node";
private static String dataNodeID = "id";
private static String dataTag = "data";
private static String dataKey = "key";
private static String dataValue = "value";
public static HashMap<String, String> getDataNode(String dataNodeIds) throws DocumentException {
String[] dataNodeId = dataNodeIds.split(",");
HashSet<String> nodeSet = new HashSet<String>();
for (String str : dataNodeId) {
nodeSet.add(str);
}
HashMap<String, String> dataMap = new HashMap<String, String>();
ArrayList<File> fs = InitPropertiesUtil.getConfigFiles(new File(PropertiesUtil.getProValue("testcase.testdata.path")), testDataFielsuffix);
for (File f : fs) {
setDataNodeMapFromFile(f, nodeSet, dataMap);
}
return dataMap;
}
@SuppressWarnings("unchecked")
public static void setDataNodeMapFromFile(File f, HashSet<String> nodeSet, HashMap<String, String> dataMap)
throws DocumentException {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(f);
Element root = document.getRootElement();
List<Element> childList = root.elements(dataNodeTag);
for (Element e : childList) {
if (nodeSet.contains(e.attributeValue(dataNodeID))) {
List<Element> dataList = e.elements(dataTag);
for (Element de : dataList) {
dataMap.put(de.attributeValue(dataKey), de.attributeValue(dataValue));
}
nodeSet.remove(e.attributeValue(dataNodeID));
}
}
return;
}
}
通过反射获得当前测试类中的声明的字段:
protected static Field[] fields = null;
private void getCurClassFields() throws SecurityException, ClassNotFoundException{
String curClassName = TestBase.getTestCaseDeclaringClass();
fields = Class.forName(curClassName).getDeclaredFields();
}
获得DataObject 标签的DataNode的值:
public String getDataObjectAnnValues(Field[] fields){
for(Field f :fields){
if(f.getAnnotation(DataObject.class)!=null){
DataObject ao = f.getAnnotation(DataObject.class);
return ao.DataNode();
}
}
return null;
}
获得测试类中声明的域的名字作为MAP的KEY:
private void fillFieldNameSet(Field[] fields, HashSet<String> fieldSet) {
for (Field f : fields) {
if(f.getAnnotation(DataObject.class) != null){
dataNodeFieldName = f.getType().getSimpleName();
fieldSet.add(f.getType().getSimpleName());
}
}
}
注入方法:
private void injectDataObj(Field[] fields, HashMap<String, Object> pageobjs)
throws IllegalArgumentException, IllegalAccessException {
for (Field f : fields) {
f.setAccessible(true);
f.set(pageobj, pageobjs.get(dataNodeFieldName));
f.setAccessible(false);
}
}
主调方法:
private HashMap<String, Object> initPageObjMap() {
HashMap<String, Object> pageobjs = new HashMap<String, Object>();
HashSet<String> fieldSet = new HashSet<String>();
try {
fillFieldNameSet(fields, fieldSet);
if(getDataObjectAnnValues(fields)!=null){
pageobjs.put(dataNodeFieldName, ParseDataXml.getDataNode(getDataObjectAnnValues(fields)));
}
injectPageObj(fields, pageobjs);
} catch (Exception e) {
e.printStackTrace();
}
return pageobjs;
}
定义类的构造方法:
public class InitDataObject{
protected static Field[] fields = null;
private String dataNodeFieldName;
public InitDataObject(Object ob) throws SecurityException, ClassNotFoundException {
getCurClassFields();
initPageObjMap();
}
在@BeforeMethod 标示的方法中初始化即可:
@BeforeMethod(alwaysRun = true)
public void initDriver(Method m) throws Exception {
new InitDataObject(this);
}