技術(shù)分享 | app自動(dòng)化測試(Android)-- 參數(shù)化用例

參數(shù)化是自動(dòng)化測試的一種常用技巧,可以將測試代碼中的某些輸入使用參數(shù)來代替。以百度搜索功能為例,每次測試搜索場景,都需要測試不同的搜索內(nèi)容,在這個(gè)過程里面,除了數(shù)據(jù)在變化,測試步驟都是重復(fù)的,這時(shí)就可以使用參數(shù)化的方式來解決測試數(shù)據(jù)變化,測試步驟不變的問題。

參數(shù)化就是把測試需要用到的參數(shù)寫到數(shù)據(jù)集合里,讓程序自動(dòng)去這個(gè)集合里面取值,每條數(shù)據(jù)都生成一條對應(yīng)的測試用例,直到集合里的值全部取完。

使用方法

使用 Appium 測試框架編寫測試用例時(shí),通常會(huì)結(jié)合單元測試框架一起使用。使用測試框架的參

  • Python 版本

<pre class="copy-codeblocks" style="font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace; font-size: 15.008px; display: block; position: relative; overflow: visible; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">@pytest.mark.parametrize("argvnames",argvalues) </pre>

  • Java 版本

<pre class="copy-codeblocks" style="font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace; font-size: 15.008px; display: block; position: relative; overflow: visible; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">@ParameterizedTest @ValueSource(strings = argvalues) </pre>

不同語言的單測框架支持的參數(shù)傳遞方式也不一樣。一般情況,會(huì)在測試用例上添加一個(gè)裝飾器,以python語言的 pytest 為例,在測試用例上添加參數(shù)化需要的裝飾器 @pytest.mark.parametrize() ,這里需要傳入兩個(gè)參數(shù) “argnamest” 與 “argvalues”,第一個(gè)參數(shù)需要一個(gè)或者多個(gè)變量來接收列表中的每組數(shù)據(jù),第二個(gè)參數(shù)傳遞存儲(chǔ)數(shù)據(jù)的列表。測試用例需要使用同名的字符串接收測試數(shù)據(jù)(與“argvnames”里面的名字一致),且列表有多少個(gè)元素就會(huì)生成并執(zhí)行多個(gè)測試用例。下面示例使用使用參數(shù)化定義三組數(shù)據(jù),每組數(shù)據(jù)都存放在一個(gè)元組中,分別將元組中的數(shù)據(jù)傳入(test_input,expected)參數(shù)中,示例代碼如下:

  • Python 版本

<pre class="copy-codeblocks" style="font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace; font-size: 15.008px; display: block; position: relative; overflow: visible; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">`# content of test_expectation.py
import pytest

@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(test_input, expected):
assert eval(test_input) == expected` </pre>

運(yùn)行結(jié)果如下:

<pre class="copy-codeblocks" style="font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace; font-size: 15.008px; display: block; position: relative; overflow: visible; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">`...
test_expectation.py ..F

test_input = '6*9', expected = 42

@pytest.mark.parametrize("test_input,expected",
[("3+5", 8), ("2+4", 6), ("6*9", 42)])

def test_eval(test_input, expected):
  assert eval(test_input) == expected

E AssertionError: assert 54 == 42
E + where 54 = eval('6*9')

test_expectation.py:6: AssertionError` </pre>

  • Java 版本

<pre class="copy-codeblocks" style="font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace; font-size: 15.008px; display: block; position: relative; overflow: visible; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class BookParamTest { @ParameterizedTest @MethodSource("intProvider") void testWithExplicitLocalMethodSource(int first,int second,int sum) { assertEquals(first + second , sum); } static Stream<Arguments> intProvider() { return Stream.of( arguments( 3 , 5 , 8), arguments( 3 , 5 , 6), arguments( 6 , 9 , 42) ); } } </pre>

運(yùn)行結(jié)果如下:

<pre class="copy-codeblocks" style="font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace; font-size: 15.008px; display: block; position: relative; overflow: visible; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">`...
org.opentest4j.AssertionFailedError:
Expected :8
Actual :6
<Click to see difference>

at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:150)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:145)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:527)
...` </pre>

上面的運(yùn)行結(jié)果可以看出,執(zhí)行的三條測試用例分別對應(yīng)三組數(shù)據(jù),測試步驟完全相同,只是傳入的測試數(shù)據(jù)發(fā)生了變化。

案例

使用“雪球”應(yīng)用,打開雪球 APP,點(diǎn)擊頁面上的搜索輸入框輸入“alibaba”,然后在搜索聯(lián)想出來的列表里面點(diǎn)擊“阿里巴巴”,選擇股票分類,獲取股票類型為“BABA”的股票價(jià)格,最后驗(yàn)證價(jià)格在預(yù)期價(jià)格的 10%范圍浮動(dòng),另一個(gè)搜索“小米”的結(jié)果數(shù)據(jù)測試步驟類似。

這個(gè)案例使用了參數(shù)化機(jī)制和 Hamcrest 斷言機(jī)制,示例代碼片斷如下:

[圖片上傳失敗...(image-a40bb3-1656485877938)]

參數(shù)化核心示例代碼:

Python 版本

<pre class="copy-codeblocks" style="font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace; font-size: 15.008px; display: block; position: relative; overflow: visible; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">`from appium import webdriver
import pytest
from hamcrest import *

class TestXueqiu:
# 省略...
# 參數(shù)化
@pytest.mark.parametrize("keyword, stock_type, expect_price", [
('alibaba', 'BABA', 170),
('xiaomi', '01810', 8.5)
])
def test_search(self, keyword, stock_type, expect_price):
# 點(diǎn)擊搜索
self.driver.find_element_by_id("home_search").click()
# 向搜索框中輸入keyword
self.driver.find_element_by_id(
"com.xueqiu.android:id/search_input_text"
).send_keys(keyword)

    # 點(diǎn)擊搜索結(jié)果
    self.driver.find_element_by_id("name").click()
    # 獲取價(jià)格
    price = float(self.driver.find_element_by_xpath(
        "http://*[contains(@resource-id, 'stockCode')\
        and @text='%s']/../../..\
        //*[contains(@resource-id, 'current_price')]"
        % stock_type
    ).text)
    # 斷言
    assert_that(price, close_to(expect_price, expect_price * 0.1))
...` </pre>

Java 版本

<pre class="copy-codeblocks" style="font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace; font-size: 15.008px; display: block; position: relative; overflow: visible; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">`import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.closeTo;
import static org.junit.jupiter.params.provider.Arguments.arguments;

public class XueqiuTest {
// 省略...

@ParameterizedTest
@MethodSource
void testSearch(String keyword, String stockType, float expectPrice) {
    //點(diǎn)擊搜索
    driver.findElement(By.id("home_search")).click();
    //向搜索框中輸入keyword
    driver.findElement(By.id("com.xueqiu.android:id/search_input_text"\
    )).sendKeys(keyword);
    //點(diǎn)擊搜索結(jié)果
    driver.findElement(By.id("name")).click();
    //獲取價(jià)格
    String format = String.format("http://*[contains(@resource-id, \
    'stockCode') and @text='%s']/../../..//*[contains(@resource-id,\
     'current_price')]", stockType);
    String text = driver.findElement(By.xpath(format)).getText();
    double price = Double.parseDouble(text);
    assertThat(price , closeTo(expectPrice,expectPrice * 0.1));

}
static Stream<Arguments> testSearch() {
    return Stream.of(
            arguments("alibaba", "BABA", 170),
            arguments("xiaomi", "01810", 8.5)
    );
}

}` </pre>

上面的代碼,傳入了兩組測試數(shù)據(jù),每組有三個(gè)數(shù)據(jù)分別為搜索關(guān)鍵詞,股票類型,及股票價(jià)格。在執(zhí)行測試用例時(shí),分別將兩組數(shù)據(jù)傳入測試步驟中執(zhí)行,對應(yīng)搜索不同的關(guān)鍵詞,使用 Hamcrest 來實(shí)現(xiàn)股票價(jià)格的斷言。
獲取更多相關(guān)資料戳:https://qrcode.ceba.ceshiren.com/link?name=article&project_id=qrcode&from=jianshu&timestamp=1656485903&author=wuyue

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容