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
无论验证点通过与否,都会去回复环境;无论回复环境正确与否都不会影响测试结果
页: 1 2 [3] 4 5 6
查看完整版本: 自己收集的一些资料,41楼贴上自己写的基于selenium的自动化框架相关的代码