自動(dòng)化測(cè)試平臺(tái)方案可行性探索

1.需求分析

隨著產(chǎn)品規(guī)模不斷完善,功能模塊逐漸增加摘完,傳統(tǒng)的人工測(cè)試逐步變得低效沼头。為了提高測(cè)試效率,可以將簡(jiǎn)單的功能測(cè)試點(diǎn)交由自動(dòng)化執(zhí)行解藻,讓測(cè)試人員可以空出更多的時(shí)間去關(guān)注業(yè)務(wù)老充,流程。而傳統(tǒng)的自動(dòng)化測(cè)試腳本由于維護(hù)性差螟左,成本高啡浊,收益低等因素導(dǎo)致難以有效的推行。而測(cè)試平臺(tái)化規(guī)劃好的話能將業(yè)務(wù)與代碼的耦合度降低胶背,有利于后期維護(hù)與可持續(xù)集成巷嚣。

2.方案設(shè)想

所有數(shù)據(jù)均保存在數(shù)據(jù)庫(kù)中,提供Web頁(yè)面進(jìn)行操作奄妨,其中

1.頁(yè)面數(shù)據(jù):源自PageObject設(shè)計(jì)模式涂籽,具體表現(xiàn)形式為一個(gè)頁(yè)面一項(xiàng)數(shù)據(jù),以米多來(lái)發(fā)管理后臺(tái)登錄頁(yè)為例:

2.自定義方法:平臺(tái)提供瀏覽器基礎(chǔ)操作并提供功能用以保存測(cè)試人員自定義的方法砸抛,例如:

平臺(tái)提供基礎(chǔ)的點(diǎn)擊元素以及向元素輸入信息基礎(chǔ)功能评雌。結(jié)合頁(yè)面數(shù)據(jù)保存登錄頁(yè)的自定義方法向用戶名輸入框中輸入用戶名,向密碼輸入框中輸入密碼直焙,向驗(yàn)證碼輸入框中輸入驗(yàn)證碼景东,點(diǎn)擊登錄按鈕等。后續(xù)通過(guò)關(guān)鍵字驅(qū)動(dòng)進(jìn)行調(diào)用奔誓。

3.自定義類方法:自定義方法并不能解決所有的需求斤吐,有些復(fù)雜的邏輯時(shí),上述的方式并不適用厨喂,所以需要通過(guò)類中實(shí)現(xiàn)邏輯方法并提供給外界調(diào)用和措。例如需求:在一個(gè)table中需要通過(guò)判斷某元素的內(nèi)容,符合預(yù)期后再進(jìn)行操作

以上三種結(jié)合起來(lái)生成測(cè)試用例蜕煌。并與測(cè)試數(shù)據(jù)形成測(cè)試用例集(關(guān)鍵字驅(qū)動(dòng)以及數(shù)據(jù)驅(qū)動(dòng)相結(jié)合的混合驅(qū)動(dòng)模式)再通過(guò)所選用配置最終形成自動(dòng)化執(zhí)行的測(cè)試腳本派阱。(配置:解決測(cè)試用例運(yùn)行環(huán)境,瀏覽器等相關(guān)問(wèn)題)然后交給執(zhí)行引擎執(zhí)行并且輸出測(cè)試報(bào)告

3.核心代碼

1.基礎(chǔ)父類

/**
 * 基礎(chǔ)父類斜纪,所有PageObject類均繼承該類
 * 
 * @author miduo
 *
 */
public class Page {
    private WebDriver driver;

    public Page(WebDriver driver) {
        super();
        this.driver = driver;
        if (this.driver != null) {
            this.driver.manage().window().maximize();
        }
    }

    public WebDriver getDriver() {
        return driver;
    }

    @Auto(key = "click")
    public void click(String locator) {
        findElement(locator).click();
    }

    @Auto(key = "input")
    public void input(String locator, String value) {
        findElement(locator).sendKeys(value);
    }

    @Auto(key = "open")
    public void open(String url) {
        driver.get(url);
    }

    /**
     * 通過(guò)反射獲取類自定義方法
     * 
     * @param key
     * @return
     */
    protected Method getCustomMethodByKey(String key) {
        Method[] methods = getClass().getMethods();
        for (Method method : methods) {
            Auto autoKey = method.getAnnotation(Auto.class);
            if (autoKey != null && autoKey.key().equals(key)) {
                return method;
            }
        }
        return null;
    }

    protected final WebElement findElement(String locator) {
        int index = locator.indexOf(".");
        String type = locator.substring(0, index).toLowerCase();
        String value = locator.substring(index + 1);
        switch (type) {
        case "id":
            return findElement(By.id(value));
        case "name":
            return findElement(By.name(value));
        case "css":
            return findElement(By.cssSelector(value));
        case "xpath":
            return findElement(By.xpath(value));
        case "class":
            return findElement(By.className(value));
        case "tag":
            return findElement(By.tagName(value));
        case "link":
            return findElement(By.partialLinkText(value));
        default:
            return null;
        }
    }

    private final WebElement findElement(By by) {
        return new WebDriverWait(driver, 10).until(new ExpectedCondition<WebElement>() {
            @Override
            public WebElement apply(WebDriver d) {
                return d.findElement(by);
            }
        });
    }
}

2.PageObject具體實(shí)現(xiàn)類

public class LoginPage extends Page {

    public LoginPage(WebDriver driver) {
        super(driver);
    }

    /**
     * PageObject自定義類方法:登錄
     * 
     * @param userName
     * @param password
     * @param captcha
     */
    @Auto(key = "login")
    public void login(String userName, String password, String captcha) {
        input("id.Account", userName);
        input("id.Password", password);
        input("id.Captcha", captcha);
        click("xpath.//input[@type='submit']");
    }
}

3.自定義方法

public class Step {
    private Class<? extends Page> clazz;
    private String methodKey;
    private Object value;

    public Step() {
        super();
    }

    public Step(Class<? extends Page> clazz, String methodKey, Object value) {
        super();
        this.clazz = clazz == null ? Page.class : clazz;
        this.methodKey = methodKey;
        this.value = value;
    }

    public Class<? extends Page> getClazz() {
        return clazz;
    }

    public void setClazz(Class<? extends Page> clazz) {
        this.clazz = clazz;
    }

    public String getMethodKey() {
        return methodKey;
    }

    public void setMethodKey(String methodKey) {
        this.methodKey = methodKey;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }
}

4.測(cè)試用例以及測(cè)試數(shù)據(jù)

public class TestCase {

    private ArrayList<Step> steps;

    public TestCase() {
        super();
        this.steps = new ArrayList<>();
    }

    public ArrayList<Step> getSteps() {
        return steps;
    }

    public void setSteps(ArrayList<Step> steps) {
        this.steps = steps;
    }

    public void addStep(Step step) {
        this.steps.add(step);
    }

    public Result perform(WebDriver driver, Parameter parameter) {
        Result result = new Result();
        for (Step step : steps) {
            Object[] value = step.getValue();
            Object[] coValue = new Object[value.length];
            Boolean change= false;
            if (parameter != null) {
                for (int i = 0; i < value.length; i++) {
                    String key = value[i].toString();
                    coValue[i] = key;
                    if (key.startsWith("${") && key.endsWith("}")) {
                        change= true;
                        Object v = parameter.get(key.substring(2, key.length() - 1));
                        value[i] = v == null ? value[i] : v;
                    }
                }
            }
            if (!step.perform(driver, value)) {
                result.setResult(false);
                return result;
            }
            if (change) {
                step.setValue(coValue);
            }
        }
        result.setResult(true);
        return result;
    }
}

public class Parameter {
    private Map<String, Object> parameter;

    public Parameter() {
        super();
        this.parameter = new HashMap<>();
    }

    public void add(String key, Object value) {
        this.parameter.put(key, value);
    }

    public void remove(String key) {
        this.parameter.remove(key);
    }

    public Object get(String key) {
        return this.parameter.get(key);
    }
}

5.執(zhí)行引擎

public class Runner {

    public static List<Result> perform(TestCase testCase, List<Parameter> testData) {
        List<Result> list = new ArrayList<>();
        System.out.println(testData.size());
        if (testData != null && testData.size() > 0) {
            for (Parameter parameter : testData) {
                // 實(shí)例化何種瀏覽器與打開(kāi)什么環(huán)境由配置決定贫母,在此不實(shí)現(xiàn)
                System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "http://chromedriver.exe");
                WebDriver driver = new ChromeDriver();
                driver.get("http://*********保密********");
                list.add(testCase.perform(driver, parameter));
            }
        } else {
            System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "http://chromedriver.exe");
            WebDriver driver = new ChromeDriver();
            driver.get("http://*********保密********");
            list.add(testCase.perform(driver, null));
        }
        return list;
    }
}

代碼僅作為可行性分析使用,并非最終版本盒刚。

測(cè)試代碼

public class Test {
    private static List<Parameter> parameters;

    static {
        Parameter parameter = new Parameter();
        parameter.add("userName", "userName_01");
        parameter.add("password", "password_01");
        parameter.add("captcha", "captcha_01");

        Parameter paramete1r = new Parameter();
        paramete1r.add("userName", "userName_02");
        paramete1r.add("password", "password_02");
        paramete1r.add("captcha", "captcha_02");
        parameters = new ArrayList<>();
        parameters.add(parameter);
        parameters.add(paramete1r);

    }
    //通過(guò)用戶自定義保存方法運(yùn)行
    @org.testng.annotations.Test
    public void test1() {
        TestCase tc = new TestCase();
        tc.addStep(new Step(null, "input", "id.Account", "${userName}"));
        tc.addStep(new Step(null, "input", "id.Password", "${password}"));
        tc.addStep(new Step(null, "input", "id.Captcha", "${captcha}"));
        tc.addStep(new Step(null, "click", "xpath.//input[@type='submit']"));
        Runner.perform(tc, parameters);
    }
    //通過(guò)自定義類方法運(yùn)行
    @org.testng.annotations.Test
    public void test2() {
        TestCase tc = new TestCase();
        tc.addStep(new Step(LoginPage.class, "login", "${userName}", "${password}", "${captcha}"));
        Runner.perform(tc, parameters);
    }
}

代碼執(zhí)行效果

再通過(guò)一個(gè)配置文件或者數(shù)據(jù)庫(kù)存儲(chǔ)PageObject類的路徑腺劣,即可通過(guò)純數(shù)據(jù)進(jìn)行驅(qū)動(dòng)測(cè)試運(yùn)行,后續(xù)再持續(xù)完善用戶因块,任務(wù)橘原,結(jié)果等相關(guān)模塊即可打造較為完善的自動(dòng)化測(cè)試平臺(tái),如此一來(lái),測(cè)試人員僅需在平臺(tái)中編輯測(cè)試用例與測(cè)試數(shù)據(jù)并新建相關(guān)測(cè)試任務(wù)即可靠柑。測(cè)試用例以及測(cè)試數(shù)據(jù)的格式為:

Page Method value
后臺(tái)登錄頁(yè) 輸入用戶名 ${userName}
后臺(tái)登錄頁(yè) 輸入密碼 ${password}
后臺(tái)登錄頁(yè) 輸入驗(yàn)證碼 ${captcha}
后臺(tái)登錄頁(yè) 點(diǎn)擊登陸 -
userName password captcha
userName1 password1 captcha1
userName2 password2 captcha2
userName3 password3 captcha3
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寨辩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子歼冰,更是在濱河造成了極大的恐慌靡狞,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隔嫡,死亡現(xiàn)場(chǎng)離奇詭異甸怕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)腮恩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)梢杭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人秸滴,你說(shuō)我怎么就攤上這事武契。” “怎么了荡含?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵咒唆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我释液,道長(zhǎng)全释,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任误债,我火速辦了婚禮浸船,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寝蹈。我一直安慰自己李命,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布箫老。 她就那樣靜靜地躺著封字,像睡著了一般。 火紅的嫁衣襯著肌膚如雪槽惫。 梳的紋絲不亂的頭發(fā)上周叮,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天辩撑,我揣著相機(jī)與錄音界斜,去河邊找鬼。 笑死合冀,一個(gè)胖子當(dāng)著我的面吹牛各薇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼峭判,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼开缎!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起林螃,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤奕删,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后疗认,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體完残,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年横漏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谨设。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缎浇,死狀恐怖扎拣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情素跺,我是刑警寧澤二蓝,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站亡笑,受9級(jí)特大地震影響侣夷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仑乌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一百拓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晰甚,春花似錦衙传、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至扁远,卻和暖如春俊鱼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背畅买。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工并闲, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谷羞。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓帝火,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子犀填,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容