首页 > 代码库 > 自动化测试-东航B2C网站测试框架搭建

自动化测试-东航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@EnableSchedulingpublic 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("/");    }}