Webdriver UI自动化测试自动装配测试用例所需的数据对象

时间:2021-10-30 09:20:34

   

        自动化测试中,维护测试数据 是一项很重要的工作,为了达到用例和测试数据的分离,我们一般会把数据存储在外部文件 或 数据库,为了简化测试,测试用例编写人员想只需要维护测试数据文件,在用例里通过简单声明,即可随意读取本次测试用例所需要的测试数据,我的思路还是为测试用例自动注入测试数据对象。

代码如下:

定义测试对象标签,只要在用例里的类域加了此标签 测试框架便认为此域变量是个测试数据对象:

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);
	}


在测试用例中声明数据对象如文章上半部分运行测试用例即可!