这个是我后来写的一本书,http://www.ituring.com.cn/minibook/10775。这个是我后来找到的自动化完美解决方案。
首先我们谈论下Automation语言组织管理,因为一个Automation 的框架要使用它,就得用语言去驱动它,在一些自动化的软件中,你会见到那种不需要写语言,直接录制回放就去执行你脚本的自动化软件,但其实在录制的过程中,它只是智能的代替你生成脚本化语言,而这语言很多时候是没有你手写的健壮(即不容易出错)。你要时不时去修改它,修改的时候你肯定必须懂得脚本语言。
我们先看一种方法,用XML去写自己的XML表达式语言然后去驱动相关的Java类或者Java方法执行.
首先,我们先说一种设计模式,叫模板方法,它是用公共的父类去制定好子类的行为,就像上一篇Junit示例那样,先定好之类可执行的方法setup()、teardown(),并在父类中指定好他们的执行顺序;就是怎么说我用父类先做好模板,子类只要继承这个模板写出自己的特定业务实现就行了。用它就像大学时你考四六级英语用的作文模板一样的用就行了。
好了,开始
首先制定一个模板类,这个模板所要做的工作是限定子类只能用什么方法,并且规定好这些方法的执行顺序。
public abstract class Demo{
public abstract void setUp();
public abstract void getValueFromXml();
public abstract void execute(Element e);
public abstract void tearDown();
public final void run(Element e){
setUp();
getValueFromXml(e);
execute();
tearDown();
}
}
分析一下这个方法,里面的setUp()和tearDown()是每个子类都会用到且用的方法一模一样,所以也可以抽出来作为第二级模板
public abstract class JSPDemo extends Demo{
SeleniumServer server;
Selenium selenium;
public void setUp() {
RemoteControlConfiguration cfg=new RemoteControlConfiguration();
try {
server=new SeleniumServer(cfg);
server.boot();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
public void tearDown() {
selenium.close();
server.stop();
}
}
接着就简单了,最后的子类即实际应用类只要继承这个第二级类就行了
public class FirstDemo extends JSPDemo {
public void getValueFromXml(Element e) {
System.out.println("getValueFromXml");
}
@Override
public void execute() {
System.out.println("execute");
}
}
这样以后每种实现类都以这种方式书写就行了。
在转回正题,我们接着讨论xml,首先我们看下面一个test.xml.
<?xml version="1.0" encoding="UTF-8"?>
<Script>
<FirstDemo name="testjingdongcom" password="jingdong" productId="286048"/>
<JingDongCustomerService name="admin" password="admin"/>
</Script>
接着我们看下解析它的类文件
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class AnalyzeScript {
private String xmlRoad;
private Document doc = null;
/**
* @param:String(xmlRoad)
* 在构造器中导入xml所在的路径
*/
public AnalyzeScript(String xmlRoad) {
this.xmlRoad = xmlRoad;
}
/**
* @param:String(xmlRoad)
* 读xml.
*/
public void parse(String xmlRoad) throws Exception {
File scriptFile = new File(xmlRoad);
if (!scriptFile.exists()) {
throw new FileNotFoundException("the script file does not exists. ");
} else if (scriptFile.isDirectory()) {
throw new IllegalArgumentException(
"the script file is not a file. ");
}
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(scriptFile);
}
/**
* @param:String(xmlRoad)
* @return NodeList
* 获得xml其中一个节点.
*/
public NodeList analyzeXmlForGetALlNodeName(){
Node fatherNode=null;
try {
parse(xmlRoad);
Element root = doc.getDocumentElement();
NodeList nodeList = doc.getElementsByTagName("Script");
System.out.println(root.getNodeName());
fatherNode = nodeList.item(0);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return fatherNode.getChildNodes();
}
/**
* @param:Element String
* @return 根据传入的element和属性名得到属性值。
*
*/
public static String analyzeXML(Element e,String attributs){
String attr=null;
try {
if(e.hasAttributes()){
attr=e.getAttribute(attributs);
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return attr;
}
}
那解析好xml后我们干什么呢?看下面代码,把xml节点名字和java类对应起来,看我们的下面这段代码,通过反射对应找到相对应得java类
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class ReflectJavaClassRun {
private String xmlRoad;
public ReflectJavaClassRun(String xmlRoad){
this.xmlRoad=xmlRoad;
}
private List scriptNodeList=new ArrayList();
/**
* 通过反射找到对应的java类
*/
public void ReflectJavaClass() throws ClassNotFoundException,Exception{
AnalyzeScript as=new AnalyzeScript(xmlRoad);
NodeList childNode=as.analyzeXmlForGetALlNodeName();
for(int i=0;i<childNode.getLength(); i++){
//得到xml节点
Node node=childNode.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element e=(Element)node;
//通过反射找到对应的java类
Class clazz=Class.forName(e.getNodeName());
Demo instance=(Demo)clazz.newInstance();
//得到刚才模板模式中定义好的run方法,注意new Class[] { Element.class }和下面的m.invoke(instance,e)都是表示在反射中如何把参数传入对应的方法中。
Method m = clazz.getMethod("run", new Class[] { Element.class });
m.invoke(instance,e);
}
}
}
好,那我们看最后一步,它调用的FirstDemo类是什么样子。
import org.w3c.dom.Element;
import com.thoughtworks.selenium.DefaultSelenium;
public class FirstDemo extends JSPDemo{
private String name;
private String password;
private String productId;
public void getValueFromXml(Element e) {
//把传入的节点中属性名为name的属性值取出来。
name=AnalyzeScript.analyzeXML(e, "name");
System.out.println(name);
//把传入的节点中属性名为passeword的属性值取出来。
password=AnalyzeScript.analyzeXML(e, "password");
//把传入的节点中属性名为productId的属性值取出来。
productId=AnalyzeScript.analyzeXML(e, "productId");
}
public void execute() {
try {
selenium=new DefaultSelenium("localhost",4444,"*firefox","http://www.360buy.com/");
selenium.start();
selenium.windowMaximize();
selenium.setTimeout("600000");
selenium.open("http://www.360buy.com/product/"+productId+".html");
Thread.sleep(20000);
selenium.click("xpath=id('InitCartUrl')");
Thread.sleep(20000);
selenium.click("xpath=id('GotoShoppingCart')");
Thread.sleep(20000);
selenium.click("xpath=id('gotoOrderInfo')");
Thread.sleep(5000);
selenium.type("xpath=id('loginname')", name);
selenium.type("xpath=id('loginpwd')", password);
selenium.click("xpath=id('loginsubmitframe')");
Thread.sleep(10000);
selenium.type("xpath=id('consignee_addressName')", "张俊卿");
selenium.select("xpath=id('consignee_province')", "index=2");
Thread.sleep(5000);
selenium.select("xpath=id('consignee_city')", "普陀区*");
Thread.sleep(10000);
selenium.select("xpath=id('consignee_county')", "桃浦新村*");
selenium.type("xpath=id('consignee_address')", "上海西站");
selenium.type("xpath=id('consignee_message')", "13999999999");
selenium.click("xpath=id('part_consignee')//div[@class='footer']/input");
Thread.sleep(5000);
selenium.check("xpath=id('IdPaymentType1')");
String s=selenium.getText("xpath=id('part_cart')//div[@class='middle']//tr[@class='align_Center']/td[1]");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
呵呵,还记得模板方法中定义的这些方法调用的顺序吗。他们会按照setUp()- getValueFromXml(e)-execute()- tearDown()的顺序先后执行。
检查一下上面的方法正确与否?运行
public static void main(String[] args) {
ReflectJavaClassRun rj=new ReflectJavaClassRun("src\\test.xml");
try {
rj.ReflectJavaClass();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
执行完后,他们接着找xml中写出的第二个类JingDongCustomerService去执行,JingDongCustomerService类如何使用模板的就不详细说了,只要继承JSPDemo就行了。
未完待续-下面小节会接着说验证是如何在xml中定义和在java类中如何使用它,对数据库的调用xml的参考格式在下面之下章节也会说到。最后说下这种方法的优缺点和改进方法。