Appium appium 跑用例时的疑惑 (测试用例的代码是和 appium 运行时的代码写在一起放在一个类里,还是测试的代码单写一个 py 文件,appium 要测试的时候导入呢)

enumerate · 2016年03月02日 · 最后由 enumerate 回复于 2016年03月15日 · 2281 次阅读

hi 大神们,我有个问题,就是在我使用 appium 跑用例时,测试代码的用例写在哪里,和 appium 的代码写在一起吗,还是单独起个 py 文件,里面写个类,然后在 appium 的代码里面调用这个类
请看我的代码

class elment(unittest.TestCase):
    def test_(self):
        desired_caps = {}
        desired_caps["platformName"] = "Android"
        desired_caps["platformVersion"] = "4.4.4"
        desired_caps["deviceName"] = "123" 
        desired_caps["udid"] = "6b716a5a"
        desired_caps["appPackage"] = "sdff"
        desired_caps["appActivity"] = "sdfsdf"
        desired_caps["unicodeKeyboard"] = "True"
        desired_caps["resetKeyboard"] = "True"
        driver = webdriver.Remote(r"http://localhost:4723/wd/hub" , desired_caps)
        sleep(10)
        el = driver.find_element_by_name("我")
        self.assertIsNotNone(el)
        el.click()
        sleep(5)
        licai = driver.find_element_by_name("你")
        self.assertIsNotNone(licai)
        licai.click()
        self.s
        driver.quit()

是写在代码里,还是想我说的专门写一个类,然后在 appium 的代码里去调用,如果在 appium 的代码里的话感觉代码会很多

共收到 17 条回复 时间 点赞

善用 unittest,unittest 有几个级别域的 setup 和 teardown。

#1 楼 @anikikun 你好,我看了一下 unittest 的 setup 它说是什么钩子,我的理解是就是把一个东西放到一个杯子里,想用的时候把杯子拿过来直接用不知道是不是这个意思

#2 楼 @enumerate 建议你了解一下 PageObject 这种模式,有助于你了解怎么设计测试用例的结构。

你的问题本身就有点奇怪,不知道你怎么定义 “appium 的代码” 和 “测试用例的代码” 。

PS:你的这份代码有问题,如果 find 不到元素会直接抛异常,而不是返回 None 。

通用的 appium 部分可以封装起来,提供给外部调用的接口,如点击按钮,触摸等。
案例最好用外部的方式进行管理,如最简单的 excel,excel 中标示这些案例中的步骤,其所对应的操作和所需的参数。
执行的时候,通过案例中的步骤驱动自动化执行。

最简单的就这样了,但还有很多需要考虑。如测试数据、结果收集、并行执行、异常处理等。

@chenhengjie123
PageObject 的模式在 Appium 下也適用嗎?最近做網頁的自動化測試,才接觸學習過。在運用 findby 的時候,除了固定的值以外,我沒辦法讓他去呼叫變數值。這樣講很模糊,直接描述一下我要做的東西,下圖是我要測試的對象,每一張圖片都代表一本書。

@FindBy(how = How.XPATH, using = "//*[@class=\"book\"]"
private List<WebElement> books;

public void openbook(String bookName){
  //處理
} 

但是最後我只覺得把畫面上所有書本的元素都找到是多此一舉,放棄這個做法。畢竟using那邊我不能使用變數值,而我很難確保每一次測試人員都擁有相同的書本,所以,轉了一下觀念,直接在處理那邊寫入我要找的元素的id,呼叫這個類的 openbook 程序的時候,才讓它開始找該網頁元素,然後進行處理。

我不知道我這樣的做法,到底有沒有符合 PageObject 設計模式的想法。想求教

#5 楼 @wkx101 PageObject 适用于使用 webdriver api 的场景。appium 也遵循了这个 api ,所以也适用的。

你上面的想法不错,我实践中没有深度使用过 PageObject ,不是很确定是否符合它的模式。不过只要能做到把 appium api 封装到业务逻辑里面,避免用例里直接调用 appium api ,应该都不错。

#3 楼 @chenhengjie123 好的我看一下你说的 pageobject 谢谢

#4 楼 @among29 好的谢谢,我看一下用 excel 的方法试试

#3 楼 @chenhengjie123 对了你好,我忘了说了,我现在是把 appium 的代码和测试用例写在一起的,就在上面图里就有
然后你说的 appium 运行代码,我的理解是使用 appium 的方法操作手机自己运行我代码定义的步骤,然后测试用例代码,就是比如我点 A 应该处 A,但是出了 B 了,那我就认为是错的

#9 楼 @enumerate 我说下我对测试代码和 appium 运行代码的定义:
测试代码:测试用例里面的代码。不包括再下一级的代码。
例如:

public void testOpenBook(){
    openbook()
}

appium 运行代码:直接调用 appium api (webdriver api) 的代码,如:

public void openbook(String bookName){
    driver.findElementByName(bookName).click()
}

因为 appium api 里面的内容都是通用的、原子型的操作,如果使用得多会导致用例长和可读性差。

#10 楼 @chenhengjie123 多谢回答,我现在遇到的问题就是,我写了一段点击按钮代码,但是要写出很多差不多重复的代码,所以在想能不能把 find 的代码,和 click 的代码等等写在一个 class 里面,需要的时候去调用

#9 楼 @enumerate
所以你所谓的用例代码其实指的就是断言。。。

#12 楼 @finelucky 测试代码是断言,我的理解不对吗,测试代码就是预期结果和实际结果不相同,不是这样写吗。。。

#11 楼 @enumerate def click_element(self,locator): 你是想要这样的封装吗?

#14 楼 @neyo 你好,是的,至今没有什么思路

#15 楼 @enumerate robotframework-appiumlibrary 是这样封装的:

def click_element(self, locator):
    """Click element identified by `locator`.

    Key attributes for arbitrary elements are `index` and `name`. See
    `introduction` for details about locating elements.
    """
    self._info("Clicking element '%s'." % locator)
    self._element_find(locator, True, True).click()

将 locator 参数(name=xxx,id=xxx)传入_element_find 方法定位元素

def _element_find(self, locator, first_only, required, tag=None):
    application = self._current_application()
    elements = self._element_finder.find(application, locator, tag)
    if required and len(elements) == 0:
        raise ValueError("Element locator '" + locator + "' did not match any elements.")
    if first_only:
        if len(elements) == 0: return None
        return elements[0]
    return elements

_element_finder 是 class ElementFinder 的实例,有如下定位策略

def __init__(self):
    self._strategies = {
        'identifier': self._find_by_identifier,
        'id': self._find_by_id,
        'name': self._find_by_name,
        'xpath': self._find_by_xpath,
        'class': self._find_by_class_name,
        'accessibility_id': self._find_element_by_accessibility_id,
        'android': self._find_by_android,
        'ios': self._find_by_ios,
        'css': self._find_by_css_selector,
        None: self._find_by_default
    }

通过_parse_locator(locator) 获取到定位策略和定位标准,调用 strategies 内定义的定位方法

def find(self, browser, locator, tag=None):
    assert browser is not None
    assert locator is not None and len(locator) > 0

    (prefix, criteria) = self._parse_locator(locator)
    strategy = self._strategies.get(prefix)
    if strategy is None:
        raise ValueError("Element locator with prefix '" + prefix + "' is not supported")
    (tag, constraints) = self._get_tag_and_constraints(tag)
    return strategy(browser, criteria, tag, constraints)

_parse_locator(locator) 对传入的定位策略 id=xxx,name=xxx 进行解析,=号前面的是定位方法,后面的是定位标准。

def _parse_locator(self, locator):
    prefix = None
    criteria = locator
    if not locator.startswith('//'):
        locator_parts = locator.partition('=')
        if len(locator_parts[1]) > 0:
            prefix = locator_parts[0].strip().lower()
            criteria = locator_parts[2].strip()
    return (prefix, criteria)

#16 楼 @neyo 好的非常感谢,我学习一下

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册