• 记我的第一次自动化尝试


    因为要反复测试一个接口,手工慢慢执行效率太低,就打算自动化实现。下面记录一下整个实现的过程和遇到的问题。

    因为开发同事有提供一个调用接口的测试方法,很自然的我就想到了要把这个方法改成用junit来实现。如果单纯一个@Test方法,那么和直接执行main方法差别不大,幸好junit提供了参数化的实现。我参考http://blog.csdn.net/zen99t/article/details/50572373之后,实现了简单的参数化,具体代码如下:

     1 @RunWith(value=Parameterized.class)
     2 public class TestTransationClient {
     3     DictionUtil du;
     4     TransactionClient tc;
     5     String url = "https://接口地址";
     6     private String row;
     7     private String authentType;
     8     private String merNo;
     9     private String IdNo;
    10     private String name;
    11     private String mobileNo;
    12     private String bankCardNo;
    13    16     public TestTransationClient(String row,String authentType,String merNo,String IdNo,String name,String mobileNo,String bankCardNo) {
    17         // TODO Auto-generated constructor stub
    18         this.row=row;
    19         this.authentType=authentType;
    20         this.merNo=merNo;
    21         this.IdNo=IdNo;
    22         this.name=name;
    23         this.mobileNo=mobileNo;
    24         this.bankCardNo=bankCardNo;
    25     }
    26     @Parameters(name="名字")
    27     public static Iterable<Object[]> data(){
    28        return Arrays.asList(new Object[][]{
    29            {"11","123456","","roy","13312345678",""},
    30             {"12","123456","123456","李白","13800138000","62213466552222222"},
    31            {"21","123456","","roy","",""},
    32             {"21","123456","","","",""}
    33         });
    34     }

    @Test方法会读取

    return Arrays.asList(new Object[][]{
    29            {"11","123456","","roy","13312345678",""},
    30             {"12","123456","","","",""},
    31            {"21","123456","","roy","",""},
    32             {"21","123456","","","",""}
    33         });
    里面的数据,每次执行读取一行,算作一条测试用例。列表存在几行数据就执行了几条测试用例,现在已经比之前每执行一条测试用例就要改一次数据方便了些。但是这样还是不够智能,我决定让他直接读取excel里面的数据,然后将运行结果写入到excel测试结果列。
    Excel的读写不难,我用的是jxl包。读excel 的方法如下
     1 /**
     2      * 读取测试用例第4列参数
     3      * 参数格式为:认证类型|商户号|身份证号|姓名|手机号|银行卡号|code|codeno|value  
     4      * 例子:11|123456|123456|roy||||1|1
     5      * @param ExcelLocation 测试用例存放路径
     6      * @return 返回Junit参数化所需要的list
     7      */
     8     @SuppressWarnings("unchecked")
     9     public List readExcel(String ExcelLocation) {
    10         jxl.Workbook readWorkBook = null;
    11         String[] temp;
    12         List list = new ArrayList<String[]>();
    13         String oraginalData = null;
    14         int k = 0;
    15         try {
    16             InputStream input = new FileInputStream(ExcelLocation);
    17             readWorkBook = Workbook.getWorkbook(input);
    18             Sheet readSheet = readWorkBook.getSheet(1);
    19             for (int i = 0; i < readSheet.getRows(); i++) {
    20                 Cell cellInput = readSheet.getCell(4, i);
    21                 Cell dataSwitch=readSheet.getCell(3, i); //设置了控制是否执行案例的开关,填enable则执行
    22                 if (cellInput.getContents().contains("|")&&dataSwitch.getContents().equals("enable")) {
    23                     oraginalData = cellInput.getContents();
    24                     // System.out.println(oraginalData);
    25                     temp = oraginalData.split("\|", 10); 
    list.add(temp);
    26 int row=i+1; 27 System.out.println("将第"+row+"行数据添加到list成功"); 28 // for (int j = 0; j < temp.length; j++) { 29 // System.out.println(temp[j]); 30 // } 31 } 32 33 } 34 35 } catch (Exception e) { 36 // TODO Auto-generated catch block 37 e.printStackTrace(); 38 } 39 return list; 40 }

    我将测试参数用“|”隔开,每个位置一个参数,读出单元格的数据后,用split方法将一串字符分割存到数组,然后再把数组存到list里面,作为junit执行需要的参数,每个数组作为一条测试用例的输入数据。

    写入Excel的方法如下:

     1 /**
     2      * 往excel写数据
     3      * @param data 需要写入的字符串
     4      * @param column 目标单元格列号
     5      * @param row     目标单元格行号
     6      * @param ExcelName  文件路径
     7      */
     8     public void writeExcel(String data,int column,int row,String ExcelName) {
     9         try {
    10             InputStream input = new FileInputStream(ExcelName);
    11             Workbook readWorkBook = Workbook.getWorkbook(input);
    12             Sheet readSheet = readWorkBook.getSheet(1);
    13             WritableWorkbook wwb = Workbook.createWorkbook(new File(ExcelName), readWorkBook);
    14             WritableSheet ws = wwb.getSheet(1);
    15             Label label = new Label(column,row, data);
    16             ws.addCell(label);
    17             int ji=row+1;
    18             System.out.println("填充第"+ji+"行表格成功");            
    19             wwb.write();
    20             System.out.println("写入表格并保存成功");
    21             wwb.close();
    22         } catch (Exception e) {
    23             // TODO Auto-generated catch block
    24             e.printStackTrace();
    25         } 
    26     }

    写这里我遇到了一个问题,就是一开始我是读取输入的文件A后重新创建一个新的文件B作为写的对象文件,每次创建的时候会把A所有的内容复制到B然后再写,这样的话导致了我执行下一条案例时也要重新创建B,那么上一条案例写入的执行结果就会丢失。解决办法是,把所有文件的命名改成一致,就是输入文件A,写完后的文件也命名为A,那么就会覆盖原来的输入文件作为新的输入文件,继续被下一条案例读取,就不会丢失之前的执行结果了。

    做到这里,我已经能够实现自动读写和执行方法调用接口了,但是还漏了一步,就是执行前可能要配置数据字典。我想过找开发要一份这个功能的代码,但是考虑到方法依赖太多,太过复杂。师姐建议我用selenium在后台页面上改。代码如下:

    public void editDiction(String code, String codeno, String value) throws InterruptedException {

    login("账号","密码");
    driver.switchTo().frame(1).findElement(By.linkText("数据字典管理")).click();
    System.out.println("进入数据字典菜单,开始修改数据字典");
    driver.switchTo().defaultContent();
    driver.switchTo().frame(driver.findElement(By.xpath("//frame[@src="popedom/manage.jsp"]"))).findElement(By.id("code")).clear();
    driver.findElement(By.id("code")).sendKeys(code);
    driver.findElement(By.cssSelector("input[type="image"]")).click();

    // driver.findElement(By.id("edit_2058595")).click();
    driver.findElement(By.xpath("//*[@alt="修改"]")).click();
    driver.findElement(By.cssSelector("Input[id^="codeNo_"]")).clear();
    driver.findElement(By.cssSelector("Input[id^="codeNo_"]")).sendKeys(codeno);
    // Thread.sleep(11111);
    // driver.findElement(By.id("codeNo_2058595")).clear();
    // driver.findElement(By.id("codeNo_2058595")).sendKeys(codeno);
    driver.findElement(By.cssSelector("Input[id^="value_"]")).clear();
    driver.findElement(By.cssSelector("Input[id^="value_"]")).sendKeys(value);
    // driver.findElement(By.id("value_2058595")).clear();
    // driver.findElement(By.id("value_2058595")).sendKeys(value);
    driver.findElement(By.xpath("//img[@alt="保存"]")).click();
    System.out.println("修改数据字典成功:" + code);
    driver.quit();
    }

    实现过程中遇到两个问题,第一,我直接执行IDE录制(IDE真是个好东西)的脚本不成功,定位不到元素。原来页面使用了frame,参考http://blog.csdn.net/lykangjia/article/details/46348535后,完美解决问题。只要加一句

    driver.switchTo().frame(1)切换frame后就能定位到了。第二,是元素ID是会变化的,类似于这种a_1、a_2,就是同一个输入框,列表不同行的ID是不一样的,为了能够复用代码,在网上找到了使用findElement(By.cssSelector("Input[id^="value_"]"))的方法完美解决。
    "Input[id^="value_"表示,id的头为value_的input元素。详见https://www.cnblogs.com/sylvia-liu/p/4469597.html
    类似的,我又写了个新增数据字典的方法
    public void addDiction(String code, String codeno, String value) throws Exception{
                login("账号","密码");
                driver.switchTo().frame(1).findElement(By.linkText("数据字典管理")).click();
                // ERROR: Caught exception [ERROR: Unsupported command [selectWindow | name=main | ]]
                driver.switchTo().defaultContent();
                driver.switchTo().frame(driver.findElement(By.xpath("//frame[@src="popedom/manage.jsp"]"))).findElement(By.id("code")).clear();
                driver.findElement(By.xpath("(//input[@type='image'])[2]")).click();
                System.out.println("开始新增数据字典");
    //            Thread.sleep(5000);
                driver.findElement(By.id("code")).clear();
                driver.findElement(By.id("code")).sendKeys(code);
                driver.findElement(By.id("value")).clear();
                driver.findElement(By.id("value")).sendKeys(codeno);
                driver.findElement(By.id("codeNo")).clear();
                driver.findElement(By.id("codeNo")).sendKeys(value);
                driver.findElement(By.cssSelector("input[type="image"]")).click();
    //            Thread.sleep(5000);
                System.out.println("新增数据字典成功:"+ code);
                driver.quit();
        }

    login方法,调起火狐并登录系统

    public void login(String account,String password){
            System.setProperty("webdriver.firefox.bin", "D:\Program Files (x86)\Mozilla Firefox\24.0\firefox.exe");
            driver = new FirefoxDriver();
            baseUrl = "后台地址";
            driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
            driver.get(baseUrl + "/user/login.jsp");
            // driver.manage().window().maximize();
            driver.findElement(By.id("userName")).click();
            driver.findElement(By.id("userName")).clear();
            driver.findElement(By.id("userName")).sendKeys(account);
            driver.findElement(By.id("UserPwd")).click();
            driver.findElement(By.id("UserPwd")).clear();
            driver.findElement(By.id("UserPwd")).sendKeys(password);
            driver.findElement(By.cssSelector("input[type="image"]")).click();
        }

    最后再遍历数据库数据字典表,遍历参数与要操作的参数做对比,参数存在则调用修改参数的方法,数据不存在则调用新增参数的方法。在使用JDBC的过程中,一开始报了DB2 SQL Error: SQLCODE=-204, SQLSTATE=42704, SQLERRMC=表名,原来是因为定位不到schema,增加 properties.setProperty("currentSchema","用户Schema");就可以了。参考链接http://bbs.csdn.net/topics/320197969

    代码如下

    public boolean dictionIsExist(String code){
             Connection conn = null;
              Statement stmt = null;
              ResultSet rs = null;
              boolean exist = false;
              try {                          
                    Properties properties = new Properties();
                    properties.setProperty("user","登录名");
                    properties.setProperty("password","密码");
                    properties.setProperty("currentSchema","用户Schema");
                    Class.forName("com.ibm.db2.jcc.DB2Driver");
                    conn=DriverManager.getConnection("jdbc:db2://数据库链接", properties);
                    stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);//ResultSet.TYPE_SCROLL_SENSITIVE类型的ResultSet才能使用rs.isLast()
                   String sql="select * from dna_diction"; stmt.executeQuery(sql); rs=stmt.getResultSet();
    int g=0; while(rs.next()){

    String dbCode
    =rs.getString("code");

    if (dbCode!=null&&code.equals(dbCode)){
    exist
    =true;
    System.out.println(
    "数据字典已存在,调用修改数据字典的方法");
    break; }

    if (rs.isLast()){
    System.out.println(
    "数据字典不存在,调用新增数据字典的方法");
    exist
    =false;
    break; } }
    rs.close();
    stmt.close();
    conn.close(); }
    catch (Exception e) {

    e.printStackTrace(); }

    return exist; } }

    最后的@Test方法如下

    import java.util.Date;
    import java.util.List;
    
    import org.junit.After;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.junit.runners.Parameterized;
    import org.junit.runners.Parameterized.Parameters;
    
    
    
    @RunWith(value=Parameterized.class)
    public class TestTransationClient {
        DictionUtil du;
        TransactionClient tc;
        String url = "接口api地址";
        private String row;
        private String authentType;
        private String merNo;
        private String IdNo;
        private String name;
        private String mobileNo;
        private String bankCardNo;
        private String code;
        private String codeno;
        private String value;
        public TestTransationClient(String row,String authentType,String merNo,String IdNo,String name,String mobileNo,String bankCardNo,String code,String codeno,String value) {
            // TODO Auto-generated constructor stub
            this.row=row;
            this.authentType=authentType;
            this.merNo=merNo;
            this.IdNo=IdNo;
            this.name=name;
            this.mobileNo=mobileNo;
            this.bankCardNo=bankCardNo;
            this.code=code;
            this.codeno=codeno;
            this.value=value;
        }
        @Parameters(name="名字{0}****")
        public static Iterable<Object[]> data(){
            List list = null;
            list=new ExcelUtil().readExcel("E:\test\AS.xls");
            return list;
        }
        @Before
        public void setUp() throws Exception {
            
            
        }
    
        @After
        public void tearDown() throws Exception {
        }
    
        @Test
        public void test() throws Exception {
            tc=new TransactionClient(url);    
            tc.setServerCert(Toolkit.getPropertyFromFile();
            String encryptKey = Toolkit.random(24);
            tc.setMerchantNo(merNo); 
            tc.setMerchantPwd("123456"); //
            tc.setTerminalNo("02028828");
    //        String transactType = authentType;//
            String address = "";//
    //        String accountno = bankCardNo;//
    //         String idcardno=bankCardNo;
    //        String name = name;// 
    //        String mobile = mobileNo;
            du=new DictionUtil();
            if(du.dictionIsExist(code)){
                du.editDiction(code,codeno,value);
            }else{
                du.addDiction(code, codeno, value);
            }
        
            
            
            AuthentXmlImpl result=tc.a(Toolkit.yyyyMMddHHmmssSSS(new Date()), encryptKey, authentType, address, bankCardNo, IdNo, name, mobileNo);
            int rowIndex=Integer.valueOf(row)-1;
            new ExcelUtil().writeExcel(result.toString(),7,rowIndex,"E:\test\AS.xls");
            if (authentType.equals("11")){
            Assert.assertEquals("0000",result.getIDCardNameResult());
            }else if(authentType.equals("12")){
                Assert.assertTrue(result.getIDCardNameResult().equals("0000"));
            }else if(authentType.equals("21")){
                Assert.assertEquals("0000", result.getIDCardNoResult());
                }
        }
    
    }

    至此,第一次自动化尝试成功,以后测试会方便很多,还可以根据需要再做调整,增加输入参数扩展都很方便。在此再次谢谢广大肯分享自己的心得体会和问题解决方案的网友们,特别感谢上述参考链接的作者们,谢谢。

    代码已去掉一些敏感信息,不便之处,敬请谅解。

    
    



  • 相关阅读:
    兼容Linux和Windows下获取视频截图
    webSocket通信
    解决IE浏览器弹出下载框的问题
    jsp的九大内置对象和四大作用域(转)
    如何确定神经网络的层数和隐藏层神经元数量
    Mac 修改系统默认Java版本
    Weka的使用
    在Mac OS系统中安装oracle
    P & NP & NPC & NP-HARD
    Dynamic programming——自下而上 VS. 自上而下
  • 原文地址:https://www.cnblogs.com/fpzh/p/7872869.html
Copyright © 2020-2023  润新知