自动化测试-东航B2C网站测试框架搭建

还是周末闲来无事做了一个我们公司电商产品-东航官网的自动化测试框架,发布到公司测试环境后获得好评。

现在把框架内容补齐做个阶段性的基线吧。

总体

1 现在框架运用到的技术: Selenium SpringMVC Hibernate Logback 

2 主要接口的定位:以主流程的页面为接口,对其进行实现

3 异常处理机制:多种策略并存克服不稳定的测试环境

4 定时任务机制:采用Spring的提供的定时器进行cron配置

5 邮件系统:采用Spring和JavaX的邮件系统

6 测试数据持久化:入库测试数据库

7 易用性:提供友好的页面供点击

8 测试用例实现多线程:测试用例类均实现runnable接口,并调用线程池处理(暂时用处不大,只有CS模式时才有用)

 

 

主要的类

BaseSeleniumManager 继承 ExpectedCondition 实现 apply方法

1 启动浏览器

2 各种杂项操作

LaputaLogin,LaputaQueryFlight,LaputaSelectFlight....继承Login,QueryFlight,SelectFlight....

1 实现基本功能的类

LoginUtil,QueryFlightUtil,SelectFlightUtil...

1 是LaputaLogin,LaputaQueryFlight,LaputaSelectFlight....的小工具类

TestCaseService实现Runnable接口的抽象类

1 是所有的测试用例的存放类

XXXCase 继承TestCaseService类并实现Runnable接口的类

1 为单个测试用例的启动类

ThreadPoolService 线程池管理类

1 提供多个测试用例一起执行的方法,采用Executors.newCachedThreadPool();方式启动线程池

 

 

package org.travelsky.autotest.selenium.model;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.travelsky.autotest.selenium.config.BaseConst;
import org.travelsky.autotest.selenium.config.StartUpType;
import org.travelsky.autotest.selenium.exception.MyExecuteMethodOutOfTimesException;
import org.travelsky.autotest.selenium.exception.MyInvokeException;
import org.travelsky.autotest.selenium.exception.MySeleniumException;
import org.travelsky.autotest.util.DateOpt;
import org.travelsky.autotest.util.RegexUtil;

public class BaseSeleniumManager implements ExpectedCondition<WebElement>, BaseSelenium {
    protected Logger log = LoggerFactory.getLogger(getClass());

    private String xpath;
    public int failCout = BaseConst.FAIL_COUNT;
    private boolean acceptNextAlert = true;

    /**
     * 启动浏览器
     */
    public WebDriver startUpBrowse(StartUpType type) {
        WebDriver driver;
        switch (type) {
        case IE:
            System.setProperty("webdriver.ie.driver", "E:\\sts-bundle\\selenium-2.42.2\\IEDriverServer.exe");
            driver = new InternetExplorerDriver();
            break;
        case FireFox:
            System.setProperty("webdriver.firefox.bin", "C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe");
            driver = new FirefoxDriver();
            break;
        case Chrome:
            System.setProperty("webdriver.chrome.bin", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe");
            driver = new ChromeDriver();
            break;
        case HtmlUnit:
            driver = new HtmlUnitDriver();
            break;
        default:
            throw new IllegalArgumentException("type is wrong");

        }
        return driver;
    }

    /**
     * 关闭driver
     * 
     * @param driver
     */
    public void close(WebDriver driver) {
        if (null == driver)
            return;
        driver.quit();
    }

    /**
     * you will not directly use this ,you should be use "checkCondition" method
     * implement ExpectedCondition
     */
    @Override
    public WebElement apply(WebDriver driver) {
        return driver.findElement(By.xpath(this.xpath));
    }

    /**
     * this method is synchronized to find and return element
     * 
     * @param driver
     * @param xpath
     *            locate element
     * @param timeOutSecond
     *            after this time ,will be throw a exception
     * @return
     */
    public WebElement checkCondition(WebDriver driver, String xpath) {
        this.xpath = xpath;
        WebDriverWait wait = new WebDriverWait(driver, BaseConst.OUT_TIME_SECOND);
        WebElement element = wait.until(this);
        return element;
    }
    
    /**
     * 自定义超时时间
     * @param driver
     * @param xpath
     * @param timeout 超时时间
     * @return
     */
    public WebElement checkCondition(WebDriver driver, String xpath,int timeout) {
        this.xpath = xpath;
        WebDriverWait wait = new WebDriverWait(driver,  timeout);
        WebElement element = wait.until(this);
        return element;
    }

    /**
     * repeat click element ,if click event have a exception will find the next
     * element in the list<WebElement> to click
     * 
     * @param elements
     * @param i
     *            start with the index = i element
     */
    public Boolean clickWebElement(List<WebElement> elements, int i) {
        int size = elements.size();
        if (i == size) {
            log.error("没有合适的按钮");
            return false;
        }
        try {
            elements.get(i).click();
            return true;
        } catch (Exception e) {
            ++i;
            this.clickWebElement(elements, i);
        }
        return true;
    }

    /**
     * 重新运行传入的方法,次数为failCout配置的数量
     * 会抛出MySeleniumException和MyInvokeException异常
     * @throws MyInvokeException
     */
    public void runBack(Method m, Throwable e, Object... args) throws MyExecuteMethodOutOfTimesException, MyInvokeException {
        if (this.failCout < 0) {
            throw new MyExecuteMethodOutOfTimesException(this.getClass()+m.getName()+ " 方法执行超过次数", e);
        }
        this.failCout--;
        try {
            m.invoke(this, args);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
            throw new MyInvokeException(this.getClass().getName()+m.getName()+"方法回滚异常",e1);
        }
    }

    /**
     * 弹出框点确认
     */
    public String AcceptAlertAndGetItsText(WebDriver driver) {
        String alertText = "";
        try {
            Alert alert = driver.switchTo().alert();
            alertText = alert.getText();
            if (acceptNextAlert) {
                alert.accept();
            } else {
                alert.dismiss();
            }
            return alertText;
        } catch (Exception e) {
            //不需要做处理
        } finally {
            acceptNextAlert = true;
        }
        return alertText;
    }

    /**
     * 关闭弹出框
     */
    public String closeAlertAndGetItsText(WebDriver driver) {
        String alertText = "";
        try {
            Alert alert = driver.switchTo().alert();
            alertText = alert.getText();
            alert.dismiss();
            return alertText;
        } catch (Exception e) {
            //不需要做处理
        }
        return alertText;
    }

    /**
     * 正则匹配替换URL的日期
     * 
     * @param url
     *            地址
     * @param regex
     *            匹配策略"\\d{6}"表示匹配6位数字
     * @param dateFormat
     *            日期格式20140404的格式为"yyyyMMdd"
     * @param addDay
     *            后移几天
     * @return
     */
    public String repalceUrl(String url, String regex, String dateFormat, int addDay) {
        String newUrl = url;
        List<String> matches = RegexUtil.matchRegex(newUrl, regex);
        for (String old : matches) {
            Date day = DateOpt.stringTypeToDateType(old, dateFormat);
            String reString = DateOpt.dateTypeToString(DateOpt.add(day, addDay), dateFormat);
            newUrl = newUrl.replace(old, reString);
        }
        return newUrl;
    }
    
    /**
     * 
     * @param ele  CHECKBOX元素
     *          
     */
    public void selectCheckBox(WebElement ele, boolean selected) {
        if (ele.isSelected()&&!selected||!ele.isSelected()&&selected) {
                ele.click();
        }

    }

    /**
     * 由于部分页面渲染速度慢,导致无法点击
     * @param second
     */
    public void delay(int second){
           try {
            Thread.sleep(second*1000);
        } catch (InterruptedException e) {
            log.error("BaseSeleniumManager的delay()方法报错",e);
        }
    }
    
    /**
     * 检查页面的title
     * @param driver
     * @param expect
     * @return
     */
    public boolean checkTitle(WebDriver driver,String expect){
        return driver.getTitle().contains(expect);
    }

}

 

 

package org.travelsky.autotest.selenium.service.manager;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.travelsky.autotest.selenium.service.TestCaseService;
/**
 * 
 * @author sunfan
 *
 */
public class ThreadPoolService {
    static Logger log = LoggerFactory.getLogger("ThreadPoolService");

    /**
     * 
     * @param testcases
     * @throws InterruptedException
     */
    public void executeTestCase(
            ArrayList<? extends Runnable> testcases)
            throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (Runnable testCase : testcases) {
            executorService.execute(testCase);
        }
    }

    /**
     * 获取当前线程池中正在运行的线程数
     * @param executorService
     * @return
     */
    public int getThreadCount(ExecutorService executorService) {
        int threadCount = ((ThreadPoolExecutor) executorService)
                .getActiveCount();
        log.info("当前正在运行的线程数:" + threadCount);
        return threadCount;
    }

}
package org.travelsky.autotest.selenium.service;

import java.util.List;

import javax.security.auth.login.LoginException;

import org.openqa.selenium.WebDriver;
import org.travelsky.autotest.selenium.config.StartUpType;
import org.travelsky.autotest.selenium.exception.MyBookException;
import org.travelsky.autotest.selenium.exception.MyExecuteMethodOutOfTimesException;
import org.travelsky.autotest.selenium.exception.MyFreePassengerException;
import org.travelsky.autotest.selenium.exception.MyInvokeException;
import org.travelsky.autotest.selenium.exception.MyLoginException;
import org.travelsky.autotest.selenium.exception.MyPassengerException;
import org.travelsky.autotest.selenium.exception.MyPaymentException;
import org.travelsky.autotest.selenium.exception.MyQueryFlightException;
import org.travelsky.autotest.selenium.exception.MySelectFlightException;
import org.travelsky.autotest.selenium.model.BaseSeleniumManager;
import org.travelsky.autotest.selenium.model.laputa.entity.QueryFlightInfo;
import org.travelsky.autotest.selenium.model.laputa.entity.User;
import org.travelsky.autotest.selenium.model.laputa.impl.LaputaDynamicAvation;
import org.travelsky.autotest.selenium.model.laputa.impl.LaputaLowPrice;
import org.travelsky.autotest.selenium.service.util.TestCaseServiceUtil;

/**
 * It‘s a entrance to provide some method to run testcase
 * 
 * @author sunfan
 * 
 */
public abstract class TestCaseService extends BaseSeleniumManager implements Runnable {
    TestCaseServiceUtil util = new TestCaseServiceUtil();
    WebDriver driver;
    
    /**
     * @param type   启动浏览器的方式, StartUpType.IE,StartUpType.FireFox
     * @param orgCity  出发城市
     * @param arrCity 到达城市
     * @param depdate  单程时间
     * @param rtDate   往返时间  (如果无往返可不填)
     * @param product 单程产品    "[productCd]", "BNGM"  "COMMON_INTER_Y_DIS", ""
     * @param rtProduct  往返产品(注意,如果为单程航班这里必须为""或者NULL)
     * @param tripType  单程还是往返
     * @param usr   登录用户
     * @param pw   登录账号
     * @param loginType  登录类型 填 ffp或者mobile
     * @throws MyQueryFlightException 
     * @throws MyPassengerException 
     * @throws MySelectFlightException 
     * @throws LoginException 
     * @throws MyBookException 
     * @throws SecurityException 
     * @throws NoSuchMethodException 
     * @throws Exception 
     */
    
    public String bookCommonFlight(StartUpType type,User user,QueryFlightInfo queryFlightInfo,List<?> paxs,boolean insurance,String testcase) throws MyLoginException,MyQueryFlightException,  MySelectFlightException,MyPassengerException, MyBookException  {
        driver = util.loginStep(type, user);
        util.queryFlightStep(driver, queryFlightInfo);
        util.selectFlightStep(driver, queryFlightInfo);
        util.passengerStep(driver, paxs, insurance);
        String res = util.bookStep(driver, testcase, user);
        return res;
    }
    
    public String bookWsdFlight(StartUpType type,User user,QueryFlightInfo queryFlightInfo,List<?> paxs,List<?> freePaxs,boolean insurance,String testcase) throws MyLoginException,MyQueryFlightException,  MySelectFlightException,MyPassengerException, MyBookException, MyFreePassengerException  {
        driver = util.loginStep(type, user);
        util.queryFlightStep(driver, queryFlightInfo);
        util.selectFlightStep(driver, queryFlightInfo);
        util.passengerStep(driver, paxs, insurance);
        util.freePassengerStep(driver, freePaxs, insurance);
        String res = util.bookStep(driver, testcase, user);
        return res;
    }
    
    public String bookAndIssueCommonTicket(StartUpType type,User user,QueryFlightInfo queryFlightInfo,List<?> paxs,boolean insurance,String payType,String testcase) throws MyLoginException,MyQueryFlightException,  MySelectFlightException,MyPassengerException, MyBookException, MyPaymentException{
        String res = this.bookCommonFlight(type, user, queryFlightInfo, paxs, insurance, testcase);
        if(res.contains("error code:"))
            return res;
        return res + "   "+util.pay(driver, payType,user);
    }
    
    public String bookAndIssueWsdTicket(StartUpType type,User user,QueryFlightInfo queryFlightInfo,List<?> paxs,List<?> freePaxs,boolean insurance,String payType,String testcase) throws MyLoginException,MyQueryFlightException,  MySelectFlightException,MyPassengerException, MyBookException, MyPaymentException, MyFreePassengerException{
        String res = this.bookWsdFlight(type, user, queryFlightInfo, paxs, freePaxs, insurance, testcase);
        if(res.contains("error code:"))
            return res;
        return res + "   "+util.pay(driver, payType,user);
    }
    
    /**
     * 航班动态
     * @param type
     * @param city1
     * @param city2
     * @return
     * @throws MyInvokeException 
     * @throws MyExecuteMethodOutOfTimesException 
     * @throws Exception
     */
    public String dynamicFlight(StartUpType type,String city1 ,String city2) throws MyExecuteMethodOutOfTimesException, MyInvokeException {
        driver = super.startUpBrowse(type);
        String res = new LaputaDynamicAvation().queryDynamicFlight(driver, city1, city2); 
        return util.addcheckResult(res, 17);
    }
    
    
    public String lowPrice(StartUpType type) throws MyExecuteMethodOutOfTimesException, MyInvokeException   {
        driver = super.startUpBrowse(type);
        String res =new LaputaLowPrice().showLowPrice(driver);
        return util.addcheckResult(res, 17);
    }

    /**
     * 实现多线程
     */
    public abstract void run();
    public abstract String execute();
    public void closeCurrentDriver() {
            super.close(this.driver);
    }

}
package org.travelsky.autotest.selenium.service.runable;

import java.util.List;

import org.travelsky.autotest.selenium.config.StartUpType;
import org.travelsky.autotest.selenium.exception.MyBookException;
import org.travelsky.autotest.selenium.exception.MyLoginException;
import org.travelsky.autotest.selenium.exception.MyPassengerException;
import org.travelsky.autotest.selenium.exception.MyPaymentException;
import org.travelsky.autotest.selenium.exception.MyQueryFlightException;
import org.travelsky.autotest.selenium.exception.MySelectFlightException;
import org.travelsky.autotest.selenium.model.laputa.entity.QueryFlightInfo;
import org.travelsky.autotest.selenium.model.laputa.entity.User;
import org.travelsky.autotest.selenium.service.TestCaseService;

public class BookAndIssueCommonTicketCase extends TestCaseService {

    StartUpType type;
    QueryFlightInfo queryFlightInfo;
    User user;
    String testcase;
    List<?> paxs;
    boolean insurance;
    String payType;

    public BookAndIssueCommonTicketCase(StartUpType type, User user, QueryFlightInfo queryFlightInfo, List<?> paxs, boolean insurance, String payType, String testcase) {
        this.type = type;
        this.queryFlightInfo = queryFlightInfo;
        this.user = user;
        this.paxs = paxs;
        this.testcase = testcase;
        this.insurance = insurance;
        this.payType = payType;
    }

    @Override
    public String execute() {
        String res = null;
        try {
            res = super.bookAndIssueCommonTicket(type, user, queryFlightInfo, paxs, insurance, payType, testcase);
        } catch (MyPaymentException e) {
            res = "支付或出票页面报错";
            log.error(res, e);
        } catch (MyQueryFlightException e) {
            res = "查询航班页面报错";
            log.error(res, e);
        } catch (MySelectFlightException e) {
            res = "选择航班页面报错";
            log.error(res, e);
        } catch (MyPassengerException e) {
            res = "旅客资料页面报错";
            log.error(res, e);
        } catch (MyBookException e) {
            res = "预定页面报错";
            log.error(res, e);
        } catch (MyLoginException e) {
            res = "登录页面报错";
            log.error(res, e);
        } finally {
            super.closeCurrentDriver();
        }
        return res;
    }

    @Override
    public void run() {
        try {
            super.bookAndIssueCommonTicket(type, user, queryFlightInfo, paxs, insurance, payType, testcase);
        } catch (Exception e) {
            log.error(this.getClass().getName(), e);
        } finally {
            super.closeCurrentDriver();
        }
    }
}
package org.travelsky.autotest.selenium.model.laputa.impl;

import java.lang.reflect.InvocationTargetException;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.travelsky.autotest.selenium.exception.MyExecuteMethodOutOfTimesException;
import org.travelsky.autotest.selenium.exception.MyInvokeException;
import org.travelsky.autotest.selenium.exception.MyQueryFlightException;
import org.travelsky.autotest.selenium.exception.MySeleniumException;
import org.travelsky.autotest.selenium.model.BaseSeleniumManager;
import org.travelsky.autotest.selenium.model.laputa.QueryFlight;
import org.travelsky.autotest.selenium.model.laputa.entity.QueryFlightInfo;
import org.travelsky.autotest.selenium.model.laputa.util.QueryFlightUtil;

/**
 * 
 * @author sunfan
 * 
 */
public class LaputaQueryFlight extends BaseSeleniumManager implements QueryFlight {

    QueryFlightUtil queryFlightUtil = new QueryFlightUtil(1);

    /**
     * 
     * @param driver
     * @param queryFlight
     * @param int input "1" = "http://easternmiles.ceair.com/flight2014/" or
     *        input "2" = http://easternmiles.ceair.com/flight/";
     * @throws MyExecuteMethodOutOfTimesException
     * @throws MyInvokeException
     * @throws MySeleniumException
     * @throws InterruptedException
     * @throws InstantiationException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws SecurityException
     * @throws NoSuchMethodException
     */
    public WebDriver queryFlight(WebDriver driver, QueryFlightInfo queryFlight) throws MyExecuteMethodOutOfTimesException, MyInvokeException {
        try {
            delay(2);
            driver.switchTo().window(driver.getWindowHandle());// 前置当前窗口
            queryFlightUtil.inputQueryInfo(driver, queryFlight);// 输入查询航班内容
            queryFlightUtil.checkJF(driver, queryFlight);// 判断是否需要点击积分兑换按钮
            delay(1);
            driver.findElement(By.id("btn_member_search")).click();// 点击查询按钮
            delay(2);
            super.AcceptAlertAndGetItsText(driver);// 点击可能出现的弹出框
            queryFlightUtil.confirmSuccess(driver);// 确认是否成功
        } catch (Exception e) {
            try {
                super.runBack(
                        this.getClass().getMethod("queryFlight", org.openqa.selenium.WebDriver.class,
                                org.travelsky.autotest.selenium.model.laputa.entity.QueryFlightInfo.class), e, driver, queryFlight);
            } catch (NoSuchMethodException | SecurityException e1) {
                log.info("反射获取login()queryFlight", e1);
            }
        }
        return driver;
    }

}
package org.travelsky.autotest.config.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@ComponentScan(basePackages="org.travelsky.autotest.*")
@EnableWebMvc
@EnableScheduling
public class WebConfig extends WebMvcConfigurerAdapter{
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resource/**").addResourceLocations("/resource/");
    }
}
package org.travelsky.autotest.config.spring;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;

public class WebInit implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext sc) throws ServletException {
        // Create the ‘root‘ Spring application context
        AnnotationConfigWebApplicationContext application = new AnnotationConfigWebApplicationContext();
        application.scan("org.travelsky.autotest.config.spring");
        // Manages the lifecycle of the root application context
        sc.addListener(new ContextLoaderListener(application));
        //CharacterEncodingFilter 
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);
        FilterRegistration filterRegistration = sc.addFilter("characterEncodingFilter", characterEncodingFilter);
        filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");

        AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
        ServletRegistration.Dynamic appServlet = sc.addServlet("appServlet", new DispatcherServlet(webContext));
        appServlet.setLoadOnStartup(1);
        appServlet.addMapping("/");
    }

}

 

 

 

 

自动化测试-东航B2C网站测试框架搭建,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。