51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

楼主: ryugun
打印 上一主题 下一主题

[资料] 自己收集的一些资料,41楼贴上自己写的基于selenium的自动化框架相关的代码

[复制链接]

该用户从未签到

41#
 楼主| 发表于 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
回复 支持 反对

使用道具 举报

该用户从未签到

42#
 楼主| 发表于 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[0]:当前数据行状态(eg:准备/测试点/恢复)
     * <p/>str[1]:动作关键字
     * <p/>str[2]:pageURL 登陆页面的URL(eg:http://XXXX/)
     * <p/>str[3]:用户名
     * <p/>str[4]:密码
     */
    public Map<String, Object> execute(Map<String, Object> context, String[] str) {
        client = new HttpClient(); //注意,此处的client是在父类BaseActionByPost 中定义的
        
        GetMethod openURLPost = new GetMethod(str[2]); //构造请求:打开登陆页面
        //利用用户名和密码构造登陆post请求URL(str[3]/用户名, str[4]/密码)
        String loginPostURL = Constants.BASEURL + "/j_spring_security_check?j_username=" + str[3] +
                                "&j_password=" + str[4] + "&_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(";")[0]; // 截取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;

    }

}
回复 支持 反对

使用道具 举报

该用户从未签到

43#
 楼主| 发表于 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;
    }
   

回复 支持 反对

使用道具 举报

该用户从未签到

44#
 楼主| 发表于 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请求的参数(egart[] 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;
    }

}
回复 支持 反对

使用道具 举报

该用户从未签到

45#
 楼主| 发表于 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[0]:当前数据行状态(eg:准备/测试点/恢复)
     * <p/>str[1]:动作关键字
     * <p/>str[2]:要导入的excel文件名(包含后缀名)
     * <p/>str[3]:父单位名称(eg:单位根目录)
     */
    public Map<String, Object> execute(Map<String, Object> context, String[] str) {
        String filePath = getParamsPath(BaseActionByPost.class.getResource("").getPath()) + str[2]; //待上传的文件路径
        int respanceCode = 0; //返回的状态码
        try {
            init(context); //初始化client和sessionID

            if (Constants.AllDEPARTMENT.equals(str[3].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[3] + "'"; //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[0] + "," + str[1] + "," + str[2] + "," + str[3] + ",【success】");
        } else {
        }
        
        context.put(Constants.CLIENT, client);
        return context;
    }

}
回复 支持 反对

使用道具 举报

该用户从未签到

46#
 楼主| 发表于 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[0]:当前数据行状态(eg:准备/测试点/恢复)
     * <p/>str[1]:动作关键字
     * <p/>str[2]:工具类型名称
     */
    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[2]));
        
        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[0] + "," + str[1] + "," + str[2] + ",【success】");
        } else {
            logger.warn(str[0] + "," + str[1] + "," + str[2] + ",【未知状态码:" + respanceCode + "】");
        }
        
        context.put(Constants.CLIENT, client);
        return context;
    }

}
回复 支持 反对

使用道具 举报

该用户从未签到

47#
 楼主| 发表于 2012-3-2 16:03:36 | 只看该作者
好了~上面已经通过请求的方式完成一些selenium难以做到的事了,我主要用来为selenium创造测试环境,以及清除测试环境,不仅快速,而且稳定(selenium的方式还是不太稳定呀~!)

接下来,我还要贴上我写的基于selenium的关键字驱动的框架的核心代码(解析器)
回复 支持 反对

使用道具 举报

该用户从未签到

48#
 楼主| 发表于 2012-3-2 16:07:22 | 只看该作者
首先要说明,这个不是教程,是我自己为了工作方便,才贴在这里的,可能大家看不懂,没关系,我自己能知道就行了~!
当然如果能对大家有帮助,那么就功德无量了~!

基于关键字驱动的框架:
我是这个思路:
1、excel里存放测试数据与测试逻辑(关键字的组合构成测试逻辑)
2、用java代码写一些基于selenium的没有任何逻辑的步骤(一个步骤对应一个关键字)部品,类似于 点击、输入呀什么的
3、利用关键字解析器读取excel,并解析里面的数据(关键字),动态调用步骤2的部品,完成测试
回复 支持 反对

使用道具 举报

该用户从未签到

49#
发表于 2012-3-2 16:33:46 | 只看该作者
xiexie
回复 支持 反对

使用道具 举报

该用户从未签到

50#
发表于 2012-3-2 16:46:25 | 只看该作者
这帖子成了书签了~
不错哦~
回复 支持 反对

使用道具 举报

该用户从未签到

51#
 楼主| 发表于 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);
    }
回复 支持 反对

使用道具 举报

该用户从未签到

52#
 楼主| 发表于 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[Constants.CELL_NUMBER_2].getContents().toString())) {
                    break; //如果未填写关键字,则跳出循环
                }
               
                if(!"all".endsWith(cell[Constants.CELL_NUMBER_1].getContents().toString())) { //当状态位不是all时
                    if(!flag.equals(cell[Constants.CELL_NUMBER_1].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
    }
回复 支持 反对

使用道具 举报

该用户从未签到

53#
 楼主| 发表于 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[Constants.CELL_NUMBER_2].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[Constants.CELL_NUMBER_2].getContents().toString().trim(), e);
            throw new AssertionError(); //抛出断言异常,退出TestNG
        } catch (ClassNotFoundException e) {
            dealWithException("ClassNotFoundException:" + keyWordsMap.get(cell[Constants.CELL_NUMBER_2].getContents().toString()), e);
        } catch (Exception e) {
            logger.error("解析关键字出错:" + cell[Constants.CELL_NUMBER_2].getContents().toString().trim(), e);
            throw new AssertionError(); //抛出断言异常,退出TestNG
        }

    }
   
    /**
     * 将Excel一行的数据转化为数组格式,并对控件路径关键字进行解析
     * @param cell excel一行的数据
     * @return 转化后的数组
     */
    private String[] changeCellToArray(Cell[] cell) {
        String[] str = new String[cell.length - 1]; //创建一个数组接收一行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[i - 1] = locatorResolver(dataMap, cell.getContents().toString()); //将控件路径关键字解析后放入数组
            } else {
                if ("null".equals(cell.getContents().toString())) {
                    str[i - 1] = ""; //输入项为null表示空白输入
                } else {
                    str[i - 1] = cell.getContents().toString();
                }
            } //else
        } //for
        
        return str;
    }
回复 支持 反对

使用道具 举报

该用户从未签到

54#
 楼主| 发表于 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[Constants.CELL_NUMBER_1]);
    }

    /**
     * 对参数文件的路径进行格式化
     * @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;
    }

}
回复 支持 反对

使用道具 举报

该用户从未签到

55#
 楼主| 发表于 2012-3-2 17:02:41 | 只看该作者
好了 解析器,已经贴完了,接下来 贴一个 关键字所对应的一个步骤部品的代码

其实就是简单的封装了下selenium的东西,因为我的业务只需要这样就能满足了,如果有复杂的步骤自己封装就是了
回复 支持 反对

使用道具 举报

该用户从未签到

56#
 楼主| 发表于 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[0]:当前数据行状态(eg:准备/测试点/恢复)
     * <p/>str[1]:动作关键字
     * <p/>str[2]:locator 控件路径
     * <p/>str[3]: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[3];
        
        try {
            selenium.type(str[2], filepath);
        } catch (SeleniumException e) {
            logger.info(str[0] + "," + str[1] + "," + str[2] + "," + str[3] + ",【fail:】", e);
            throw new AssertionError();
        }
        logger.info(str[0] + "," + str[1] + "," + str[2] + "," + str[3] + ",【success】");
        context.put(Constants.SELENIUM, selenium);
        return context;
    }
   
}
回复 支持 反对

使用道具 举报

该用户从未签到

57#
 楼主| 发表于 2012-3-2 17:17:11 | 只看该作者
接下来再看看测试数据保存文件是什么样子的呢?

既excel里该怎么写呢?(excel是保存测试数据和测试逻辑的文件)
回复 支持 反对

使用道具 举报

该用户从未签到

58#
 楼主| 发表于 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
回复 支持 反对

使用道具 举报

该用户从未签到

59#
 楼主| 发表于 2012-3-2 17:33:54 | 只看该作者
接下来,就是测试用例java文件了

其实,这个文件不需要就可以直接运行了,我为什么要在这里加这个文件呢?主要是业务需要,不解释了,核心的地方都在上面的代码里。
回复 支持 反对

使用道具 举报

该用户从未签到

60#
 楼主| 发表于 2012-3-2 17:41:41 | 只看该作者
回复 59# ryugun

对于这里的标志位可能大家不太理解,这个也是和业务相关的,我才加在这里,可能大家都不需要,还是说说我的看法,为什么会加在这里,我也是思考了很久才加上的,在最开始设计的时候是没有这个状态位的。

由于我们项目目前的情况,一个用例其实应该分为3个部分(手工操作也是如此):准备测试环境-->测试验证点--->回复测试环境以免影响其他用例

其实我们真正关心的只是测试验证点,然后在跑自动化的时候,由于selenium存在不稳定性,往往在准备环境或回复环境的时候失败,验证点正常。这样就会给我们返回一个错误信息:我的验证点是正确的,用例通过的,但其他地方错了,导致自动化跑失败~!

因此,为了解决这个问题,在运行用例的时候,我就要设计一下,让准备环境和回复环境不能影响我的测试结果。

我是这么做的:利用testNG的 beforClass  和 AfterClass,以及强依赖关系depends

当准备环境失败,我就不会去执行测试验证点(必然失败),testNG会给我标识为skip
当准备环境成功,才会执行验证点,验证点通过既pass,验证点失败既fail
无论验证点通过与否,都会去回复环境;无论回复环境正确与否都不会影响测试结果
回复 支持 反对

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-11-23 02:07 , Processed in 0.090320 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表