自动化测试中,维护测试数据 是一项很重要的工作,为了达到用例和测试数据的分离,我们一般会把数据存储在外部文件 或 数据库,为了简化测试,测试用例编写人员想只需要维护测试数据文件,在用例里通过简单声明,即可随意读取本次测试用例所需要的测试数据,我的思路还是为测试用例自动注入测试数据对象。
代码如下:
定义测试对象标签,只要在用例里的类域加了此标签 测试框架便认为此域变量是个测试数据对象:
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); }