ryugun
发表于 2012-3-2 15:27:11
所需要的jar包:
commons-codec-1.5.jar
commons-httpclient-3.1.jar
commons-logging-1.1.1.jar
httpclient-4.1.1.jar
httpcore-4.1.jar
httpmime-4.1.1.jar
ryugun
发表于 2012-3-2 15:30:37
本帖最后由 ryugun 于 2012-3-2 16:45 编辑
先是通过post请求方式登陆:
代码如下:
关键代码在里面,其他的代码请不要关注:
步骤1:先是通过post请求方式登陆:(为什么要登陆呢?主要是 取得session 以及 对付权限问题:没有登陆就不能做其他事呀~)
代码如下:
关键代码在里面,其他的代码请不要关注:
package com.XXXXX.XXXX.action.bypost.common;
import java.io.IOException;
import java.util.Map;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import com.XXXX.Constants;
/**
* 通过Post请求的方式登陆
*
* <p/>excel对应关键字:loginByPost
*
*/
public class LoginByPost extends BaseActionByPost {
/**
* 通过Post请求的方式登陆
* @return context aw参数传递上下文对象
* @param context aw参数传递上下文对象
* @param str 包含各种参数的数组(从用例数据文件里读取出的一行数据)
* <p/>str:当前数据行状态(eg:准备/测试点/恢复)
* <p/>str:动作关键字
* <p/>str:pageURL 登陆页面的URL(eg:http://XXXX/)
* <p/>str:用户名
* <p/>str:密码
*/
public Map<String, Object> execute(Map<String, Object> context, String[] str) {
client = new HttpClient(); //注意,此处的client是在父类BaseActionByPost 中定义的
GetMethod openURLPost = new GetMethod(str); //构造请求:打开登陆页面
//利用用户名和密码构造登陆post请求URL(str/用户名, str/密码)
String loginPostURL = Constants.BASEURL + "/j_spring_security_check?j_username=" + str +
"&j_password=" + str + "&_spring_security_remember_me=null";
GetMethod loginPost = new GetMethod(loginPostURL); //构造请求:登陆
Header[] getLoginPageHeaders = null; //headers
try {
client.executeMethod(openURLPost); //提交Post请求并返回状态码
getLoginPageHeaders = openURLPost.getResponseHeaders();
String cook_session_id = null; // 在返回的cookie中获取session_id相关内容
for (Header getLoginPageHeader : getLoginPageHeaders) {
if (getLoginPageHeader.getName().equals("Set-Cookie")) {
cook_session_id = getLoginPageHeader.getValue();
}
}
sessionIDByPost = cook_session_id.split(";"); // 截取session
if (null == sessionIDByPost) {
throw new AssertionError();
}
loginPost.addRequestHeader(Constants.COOKIE, sessionIDByPost); //将sessionID设置到登陆请求里
int respanceCode = client.executeMethod(loginPost); //提交Post请求并返回状态码
if (200 == respanceCode) { //判断返回的状态码是否正确
} else {
}
} catch (HttpException e) {
} catch (IOException e) {
} catch (Exception e) {
} finally {
openURLPost.releaseConnection(); //释放
loginPost.releaseConnection();
}
context.put(Constants.SESSIONIDBYPOST, sessionIDByPost); //设置sessionID
context.put(Constants.CLIENT, client);
return context;
}
}
ryugun
发表于 2012-3-2 15:43:28
接下来,把上面提到的BaseActionByPost代码贴出来
我自己在做的时候,通过Post请求方式的类都会继承这个父类,里面写了一些公用的方法
package com.XXXX.action.bypost.common;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import com.XXXX.Constants;
import com.XXXX.action.common.BaseAction;
/**
* Action基类(通过Post请求)
*
*/
public abstract class BaseActionByPost implements BaseAction {
/**
* client对象
*/
public HttpClient client = null;
/**
* sessionID
*/
public String sessionIDByPost = "";
/**
* 执行动作
* @param context aw参数传递上下文Map对象
* @param str 包含各种参数的数组(从用例数据文件里读取出的一行数据)
* @return aw参数传递上下文Map对象
*/
public abstract Map<String, Object> execute(Map<String, Object> context, String[] str);
/**
* 构造Get请求并提交(GetMethod)
* @param encodURL 待提交的Get请求
* @return 提交请求后返回的状态码
*/
public int doGet(String encodURL) {
GetMethod get = new GetMethod(encodURL); //构造请求
get.addRequestHeader(Constants.COOKIE, sessionIDByPost); //为请求设置cookie
int respanceCode = 0; //状态码
try {
respanceCode = client.executeMethod(get); //提交Post请求并返回状态码
} catch (HttpException e) {
} catch (IOException e) {
} catch (Exception e) {
} finally {
get.releaseConnection(); //释放连接
}
return respanceCode;
}
/**
* 构造Post请求并提交(PostMethod)
* @param encodURL 待提交的Post请求
* @return 提交请求后返回的状态码
*/
public int doPost(String encodURL) {
PostMethod post = new PostMethod(encodURL); //构造请求
post.addRequestHeader(Constants.COOKIE, sessionIDByPost); //为请求设置cookie
int respanceCode = 0; //状态码
try {
respanceCode = client.executeMethod(post); //提交Post请求并返回状态码
} catch (HttpException e) {
} catch (IOException e) {
} catch (Exception e) {
} finally {
post.releaseConnection(); //释放连接
}
return respanceCode;
}
ryugun
发表于 2012-3-2 15:44:56
接着是BaseACtionByPost的代码,上面还没有贴完呢
/**
* 初始化client和sessionID
* @param context aw参数传递上下文对象
*/
public void init(Map<String, Object> context) {
client = (HttpClient) context.get(Constants.CLIENT);
if (null == client || null == context.get("sessionIDByPost")) {
logger.error("初始化client或sessionID失败:client或sessionID为空!");
throw new AssertionError();
}
sessionIDByPost = context.get("sessionIDByPost").toString(); //session
}
/**
* 上传文件(提交上传文件的请求)
* @param encodURL 待提交的Post请求
* @param parts post请求的参数(egimages/smilies/default/tongue.gifart[] parts = {new FilePart("filedata", file)};)
* @return 提交请求后返回的状态码
*/
public int upload(String encodURL, Part[] parts) {
PostMethod filePost = new PostMethod(encodURL); //构造请求
filePost.addRequestHeader(Constants.COOKIE, sessionIDByPost); //为请求设置cookie
int respanceCode = 0;
try {
filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost.getParams()));
respanceCode = client.executeMethod(filePost);
} catch (HttpException e) {
} catch (IOException e) {
} catch (Exception e) {
} finally {
filePost.releaseConnection();
}
return respanceCode;
}
/**
* 打开一个文件
* @param filePath 文件路径
* @return
*/
public File getFile(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
logger.error("待上传文件不存在!" + filePath);
throw new AssertionError();
}
return file;
}
/**
* 对字符串进行转码
* @param str 待转码的字符串
* @return String 转码后的字符串
*/
public String encod(String str) {
String encodURL = str;
try {
encodURL = java.net.URLEncoder.encode(str, "UTF-8"); //转码
} catch (UnsupportedEncodingException e) {
logger.warn("转码失败:" + str, e);
}
return encodURL;
}
/**
* 取工程目录下参数目录路径
* @param path 当前类class文件所在路径
* @return Params目录路径
*/
public static String getParamsPath(String path) {
path = path.replaceAll("bin/com/XXXX/action/bypost/common/", "");
path = path.replaceFirst("/", "").trim();
path = path.replaceAll("/", "\\\\");
path = path + "params\\";
try {
path = URLDecoder.decode(path, "UTF-8"); //对URL路径中的编码进行解码
} catch (UnsupportedEncodingException e) {
logger.warn("文件路径解码出现异常!", e);
}
return path;
}
}
ryugun
发表于 2012-3-2 15:53:22
本帖最后由 ryugun 于 2012-3-2 15:58 编辑
步骤2:在步骤1登陆成功的基础上(主要是获取session),以上传一个东西为例(selenium上传东西是弱项)
package com.XXX.action.bypost.system.blanch;
import java.util.Map;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import com.XXX.Constants;
import com.XXX.action.bypost.common.BaseActionByPost;
import com.XXX.action.bypost.common.GetIdByQuerySQL;
/**
* 通过Post请求的方式导入单位
*
* <p/>excel对应关键字:importBlanchByPost
*
*/
public class ImportBlanchByPost extends BaseActionByPost {
/**
* 导入单位的PostURL
*/
public static final String POSTURL = Constants.BASEURL + "/department/importDepartment.do";
/**
* 通过Post请求的方式导入单位
* @return context aw参数传递上下文对象
* @param context aw参数传递上下文对象
* @param str 包含各种参数的数组(从用例数据文件里读取出的一行数据)
* <p/>str:当前数据行状态(eg:准备/测试点/恢复)
* <p/>str:动作关键字
* <p/>str:要导入的excel文件名(包含后缀名)
* <p/>str:父单位名称(eg:单位根目录)
*/
public Map<String, Object> execute(Map<String, Object> context, String[] str) {
String filePath = getParamsPath(BaseActionByPost.class.getResource("").getPath()) + str; //待上传的文件路径
int respanceCode = 0; //返回的状态码
try {
init(context); //初始化client和sessionID
if (Constants.AllDEPARTMENT.equals(str.trim())) { //父单位选择 全部单位
Part[] parts = {new FilePart("fileData", getFile(filePath)),
new StringPart("parentId", "0")}; //父单位ID
respanceCode = upload(POSTURL, parts); //提交请求并返回状态码(这里的upload函数就是在父类BaseActionBypost里面写好的,下面的doPost也是一样的)
} else {
String sql = "select id from department where name='" + str + "'"; //sql语句
GetIdByQuerySQL query = new GetIdByQuerySQL(); //查询出对应父单位在mysql中的id(这里是我自己方便得到ID,去直接查询Mysql)
Part[] parts = {new FilePart("fileData", getFile(filePath)),
new StringPart("parentId", query.execute(sql, "id"))}; //父单位ID
respanceCode = upload(POSTURL, parts); //提交请求并返回状态码
}
} catch (Exception e) {
throw new AssertionError();
}
if (Constants.RESPANCECODE == respanceCode) { //判断返回的状态码是否正确
logger.info(str + "," + str + "," + str + "," + str + ",【success】");
} else {
}
context.put(Constants.CLIENT, client);
return context;
}
}
ryugun
发表于 2012-3-2 15:56:06
步骤2:上面是通过Post请求方式上传东西,这个是提交一个普通请求的代码,其实和login差不多,还是做个列子吧
package com.huaweisymantec.iget.omm.action.bypost.system.tooltype;
import java.util.Map;
import com.XXX.Constants;
import com.XXX.action.bypost.common.BaseActionByPost;
/**
* 系统管理-工具类型:通过Post请求的方式添加工具类型
*
* <p/>excel对应关键字:createToolTypeByPost
*
*/
public class CreateToolTypeByPost extends BaseActionByPost {
/**
* 系统管理-工具类型:通过Post请求的方式添加工具类型
* @return context aw参数传递上下文对象
* @param context aw参数传递上下文对象
* @param str 包含各种参数的数组(从用例数据文件里读取出的一行数据)
* <p/>str:当前数据行状态(eg:准备/测试点/恢复)
* <p/>str:动作关键字
* <p/>str:工具类型名称
*/
public Map<String, Object> execute(Map<String, Object> context, String[] str) {
StringBuffer encodURL = new StringBuffer(); //转码后的URL
encodURL.append(Constants.BASEURL);
encodURL.append("/servercfg/addToolType.do?");
encodURL.append("name="); //工具类型名称
encodURL.append(encod(str));
int respanceCode = 0; //返回的状态码
try {
init(context); //初始化client和sessionID
respanceCode = doPost(encodURL.toString()); //提交请求并返回状态码
} catch (Exception e) {
throw new AssertionError();
}
if (Constants.RESPANCECODE == respanceCode) { //判断返回的状态码是否正确
logger.info(str + "," + str + "," + str + ",【success】");
} else {
logger.warn(str + "," + str + "," + str + ",【未知状态码:" + respanceCode + "】");
}
context.put(Constants.CLIENT, client);
return context;
}
}
ryugun
发表于 2012-3-2 16:03:36
好了~上面已经通过请求的方式完成一些selenium难以做到的事了,我主要用来为selenium创造测试环境,以及清除测试环境,不仅快速,而且稳定(selenium的方式还是不太稳定呀~!)
接下来,我还要贴上我写的基于selenium的关键字驱动的框架的核心代码(解析器)
ryugun
发表于 2012-3-2 16:07:22
首先要说明,这个不是教程,是我自己为了工作方便,才贴在这里的,可能大家看不懂,没关系,我自己能知道就行了~!
当然如果能对大家有帮助,那么就功德无量了~!
基于关键字驱动的框架:
我是这个思路:
1、excel里存放测试数据与测试逻辑(关键字的组合构成测试逻辑)
2、用java代码写一些基于selenium的没有任何逻辑的步骤(一个步骤对应一个关键字)部品,类似于 点击、输入呀什么的
3、利用关键字解析器读取excel,并解析里面的数据(关键字),动态调用步骤2的部品,完成测试
wujinjie0346
发表于 2012-3-2 16:33:46
xiexie
bingorz
发表于 2012-3-2 16:46:25
这帖子成了书签了~
不错哦~
ryugun
发表于 2012-3-2 16:53:58
好~解析器的代码如下:
package comXXX.resolver;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.httpclient.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.XXX.Constants;
import com.thoughtworks.selenium.Selenium;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
/**
* 关键字解析器
* <p />解析出用例文件里每一行的动作步骤/控件路径关键字,调用部品执行测试步骤
*
*/
public class KeyWordsResolver {
/**
* Log
*/
private static Logger logger = LoggerFactory.getLogger(KeyWordsResolver.class);
/**
* selenium对象
*/
private Selenium selenium = null;
/**
* client对象
*/
private HttpClient client = null;
/**
* aw参数传递上下文对象
*/
private Map<String, Object> context = new HashMap<String, Object>();
/**
* 数据地图(控件路径、浏览器参数)
*/
private Map<String, String> dataMap = new HashMap<String, String>();
/**
* 关键字地图
*/
private Map<String, String> keyWordsMap = new HashMap<String, String>();
/**
* 关键字解析器-构造函数
* @param locatorMap 控件路径容器地图
* @param keyWordsMap 关键字地图
*/
public KeyWordsResolver(Map<String, String> dataMap, Map<String, String> keyWordsMap) {
this.dataMap = dataMap;
this.keyWordsMap = keyWordsMap;
context.put("selenium", selenium);
context.put("client", client);
}
ryugun
发表于 2012-3-2 16:54:50
接着上面的 解析器,该类还没贴完呢~
/**
* 读取用例文件并把每一行的数据进行解析
* @param caseFile 用例文件路径
* @param caseId 用例编号(一个sheet表示一个用例,sheet名称)
* @param flag 指定读取带有相应标志位的数据行(prepare/test/renew/all),例如:填写prepare,则只读取标志位为prepare的数据行
* @return Selenium
*/
public Selenium readFile(String caseFile, String caseId, String flag) {
logger.info("********************TestCase: " + caseId + "," + flag + " Start********************");
//取得文件的path
String path = formatPath(KeyWordsResolver.class.getResource("").getPath(), caseFile);
InputStream is = null; //打开一个文件读取流
Workbook rwb = null; //打开一个xls的workbook
try {
is = new FileInputStream(path);
rwb = Workbook.getWorkbook(is);
Sheet rs = rwb.getSheet(caseId); //获取一个工作sheet
int rowsLength = rs.getRows(); //获取当前sheet的行数
//循环读取出每一行的内容,根据关键字执行测试步骤
for (int i = Constants.CELL_NUMBER_1; i < rowsLength; i++) { //从第2行开始取数据,第一行是注释
Cell[] cell = rs.getRow(i); //获取一行的数据,放进一个数组里
if("".equals(cell.getContents().toString())) {
break; //如果未填写关键字,则跳出循环
}
if(!"all".endsWith(cell.getContents().toString())) { //当状态位不是all时
if(!flag.equals(cell.getContents().toString())) {
continue; //如果当前数据行的状态位不是期待的状态位,则跳过此循环
}
}
actionResolver(cell); //解析一行的关键字,调用对应部品执行测试步骤
}
} catch (FileNotFoundException e) {
dealWithException("文件不存在:" + path, e);
}catch (AssertionError e) {
if(null != selenium) {
selenium.stop(); //关闭浏览器
}
throw new AssertionError(); //抛出新异常,退出TestNG
} catch (Exception e) {
dealWithException("未知异常,用例失败:" + path, e);
}finally {
rwb.close();
try {
is.close();
} catch (IOException e) {
logger.warn("***关闭用例文件流出错:" + path + "***");
logger.warn(e.getMessage());
}
logger.info("********************TestCase: " + caseId + "," + flag+ " End********************");
}
return selenium;
}
/**
* 处理异常,并关闭selenium
* @param loggerMessage 要打入log的自定义异常信息
* @param e 异常
*/
private void dealWithException(String loggerMessage, Exception e) {
logger.error(loggerMessage, e);
if(null != selenium) {
selenium.stop();//关闭浏览器
}
throw new AssertionError(); //抛出断言异常,退出TestNG
}
ryugun
发表于 2012-3-2 16:56:27
继续接着上面的解析器,还是没有贴完呀~~~
/**
* 步骤关键字解析器
* <p/>解析用例文件的步骤关键字,调用部品执行测试步骤
* @param cell excel里的一行数据
*/
@SuppressWarnings("unchecked")
private void actionResolver(Cell[] cell) {
// 根据excel表格每一行第一个单元格的内容,执行对应的步骤
try {
Class<?> action = Class.forName(keyWordsMap.get(cell.getContents().toString()));
this.setContext((Map<String, Object>) action.getMethod
("execute", new Class[]{Map.class, String[].class}).
invoke(action.newInstance(), new Object[]{context, changeCellToArray(cell)}));
} catch (InvocationTargetException e) {
logger.error("Selenium运行出错,请检查控件路径是否正确!关键字:"
+ cell.getContents().toString().trim(), e);
throw new AssertionError(); //抛出断言异常,退出TestNG
} catch (ClassNotFoundException e) {
dealWithException("ClassNotFoundException:" + keyWordsMap.get(cell.getContents().toString()), e);
} catch (Exception e) {
logger.error("解析关键字出错:" + cell.getContents().toString().trim(), e);
throw new AssertionError(); //抛出断言异常,退出TestNG
}
}
/**
* 将Excel一行的数据转化为数组格式,并对控件路径关键字进行解析
* @param cell excel一行的数据
* @return 转化后的数组
*/
private String[] changeCellToArray(Cell[] cell) {
String[] str = new String; //创建一个数组接收一行excel的数据,
//由于第一列为注释,不需要写入数组,因此数组长度 = cell长度 - 1
for (int i = Constants.CELL_NUMBER_1; i < cell.length; i++) { //第一列为注释,因此跳过,直接从第2列开始读数据
if ("".equals(cell.getContents().toString())) {
continue; //当一个表格没有内容时,跳过本次解析
}
//标识为 XML: 的数据表示在xml文件里定义的locator
if (cell.getContents().toString().startsWith("XML:")) {
str = locatorResolver(dataMap, cell.getContents().toString()); //将控件路径关键字解析后放入数组
} else {
if ("null".equals(cell.getContents().toString())) {
str = ""; //输入项为null表示空白输入
} else {
str = cell.getContents().toString();
}
} //else
} //for
return str;
}
ryugun
发表于 2012-3-2 16:57:07
继续解析器,最后一点了~~
/**
* 控件路径解析器
* @param map 存放对应控件的地图
* @param locatorKeyWord 需要解析的控件路径关键字
* <p/> 格式:XML:login_submit(login_submit为控件路径key, XML:为标识)
* @return 控件路径
*/
private String locatorResolver(Map<String, String> map, String locatorKeyWord) {
String str[] = locatorKeyWord.split(":"); //将控件路径关键字按格式拆分
return map.get(str);
}
/**
* 对参数文件的路径进行格式化
* @param path 当前java文件的路径
* @param fileName 文件名称
* @return path 格式后的字符串
*/
private String formatPath(String path, String fileName) {
path = path.replaceAll("bin/com/huaweisymantec/iget/omm/resolver/", "");
path = path.replaceAll("\\/", "\\\\");
path = path.replaceFirst("\\\\", "").trim();
path = path + "params\\casedata\\" +fileName;
try {
path = URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
logger.warn(e.getMessage(), e);
}
return path;
}
/**
* 取得Selenium对象
* @return
*/
public Selenium getSelenium() {
return selenium;
}
/**
* 设置Selenium对象
* @param selenium
*/
public void setSelenium(Selenium selenium) {
this.selenium = selenium;
}
/**
* 取得关键字地图
* @return
*/
public Map<String, String> getKeyWordsMap() {
return keyWordsMap;
}
/**
* 设置关键字地图
* @param keyWordsMap
*/
public void setKeyWordsMap(Map<String, String> keyWordsMap) {
this.keyWordsMap = keyWordsMap;
}
public Map<String, String> getDataMap() {
return dataMap;
}
public void setDataMap(Map<String, String> dataMap) {
this.dataMap = dataMap;
}
/**
* 取得aw参数传递上下文对象
* @return context
*/
public Map<String, Object> getContext() {
return context;
}
/**
* 设置aw参数传递上下文对象
* @param context
*/
public void setContext(Map<String, Object> context) {
this.context = context;
setSelenium((Selenium) context.get("selenium"));
setClient((HttpClient) context.get("client"));
}
public HttpClient getClient() {
return client;
}
public void setClient(HttpClient client) {
this.client = client;
}
}
ryugun
发表于 2012-3-2 17:02:41
好了 解析器,已经贴完了,接下来 贴一个 关键字所对应的一个步骤部品的代码,
其实就是简单的封装了下selenium的东西,因为我的业务只需要这样就能满足了,如果有复杂的步骤自己封装就是了
ryugun
发表于 2012-3-2 17:15:48
这个代码是 一个步骤代码(关键字所对应的一个步骤):往上传文件输入框里输入文件路径(手动操作的话,点击上传按钮,就弹出windows的文件选择对话框,由于selenium不支持,所以就直接往输入框里输入对应的文件路径就可以了)
package com.XXX.action.common;
import java.util.Map;
import com.XXX.Constants;
import com.XXX.action.bypost.common.BaseActionByPost;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.SeleniumException;
/**
* 往上传输入框里输入待上传的文件路径
*
* <p/>部品层次:底层
* <p/>excel对应关键字:uploadInput
* @author lKF49150
*/
public class UploadInput implements BaseAction {
/**
* 往上传输入框里输入待上传的文件路径
* @param context aw参数传递上下文对象
* @param str 包含各种参数的数组(从用例数据文件里读取出的一行数据)
* @return context aw参数传递上下文对象
* <p/>str:当前数据行状态(eg:准备/测试点/恢复)
* <p/>str:动作关键字
* <p/>str:locator 控件路径
* <p/>str:expected 要输入的文件路径(空白输入,请输入null)
*/
public Map<String, Object> execute(Map<String, Object> context, String[] str) {
Selenium selenium = (Selenium) context.get(Constants.SELENIUM);
String filepath = //上传文件的路径
BaseActionByPost.getParamsPath(BaseActionByPost.class.getResource("").getPath()) + str;
try {
selenium.type(str, filepath);
} catch (SeleniumException e) {
logger.info(str + "," + str + "," + str + "," + str + ",【fail:】", e);
throw new AssertionError();
}
logger.info(str + "," + str + "," + str + "," + str + ",【success】");
context.put(Constants.SELENIUM, selenium);
return context;
}
}
ryugun
发表于 2012-3-2 17:17:11
接下来再看看测试数据保存文件是什么样子的呢?
既excel里该怎么写呢?(excel是保存测试数据和测试逻辑的文件)
ryugun
发表于 2012-3-2 17:28:07
本帖最后由 ryugun 于 2012-3-2 17:29 编辑
数据保存文件的写法(不能上传文件,所以只能手写了,不然我贴这么多代码干嘛,直接贴java文件就可以了):
一个xls文件代表一个测试套(里面包含了同板块相关的所有用例),一个sheet代表一个用例,一行数据代表一个步骤,一列数据代表一个用例的测试逻辑
例如:登陆用例
文件名:case-login.xls(名字随便,表示该excel里全部放的都是登陆板块的用例,但在后面测试用例java文件里要对应上)
sheet名:TestCase-Login-001(可以随便命名,但在后面测试用例java文件里要对应上)
第一行,第一列(注释) 标志位(准备环境/测试验证点/回复环境) 关键字 参数1 参数2 参数n
准备环境:用post方式登陆 prepare loginByPost admin 12345678
上传一个文件作为测试数据 prepare upLoadByPost 待上传文件.xls
测试验证点:selenium登陆 test openURL http://XXX.XXXX
登陆 test login //input[@id='username'] tester 密码xpath 123456 提交按钮xpath
…………………………等等
回复环境:删除文件byPost renew delFileByPost 待上传文件.xls
ryugun
发表于 2012-3-2 17:33:54
接下来,就是测试用例java文件了
其实,这个文件不需要就可以直接运行了,我为什么要在这里加这个文件呢?主要是业务需要,不解释了,核心的地方都在上面的代码里。
ryugun
发表于 2012-3-2 17:41:41
回复 59# ryugun
对于这里的标志位可能大家不太理解,这个也是和业务相关的,我才加在这里,可能大家都不需要,还是说说我的看法,为什么会加在这里,我也是思考了很久才加上的,在最开始设计的时候是没有这个状态位的。
由于我们项目目前的情况,一个用例其实应该分为3个部分(手工操作也是如此):准备测试环境-->测试验证点--->回复测试环境以免影响其他用例
其实我们真正关心的只是测试验证点,然后在跑自动化的时候,由于selenium存在不稳定性,往往在准备环境或回复环境的时候失败,验证点正常。这样就会给我们返回一个错误信息:我的验证点是正确的,用例通过的,但其他地方错了,导致自动化跑失败~!
因此,为了解决这个问题,在运行用例的时候,我就要设计一下,让准备环境和回复环境不能影响我的测试结果。
我是这么做的:利用testNG的 beforClass和 AfterClass,以及强依赖关系depends
当准备环境失败,我就不会去执行测试验证点(必然失败),testNG会给我标识为skip
当准备环境成功,才会执行验证点,验证点通过既pass,验证点失败既fail
无论验证点通过与否,都会去回复环境;无论回复环境正确与否都不会影响测试结果