0. 前言
在这里记录一些在测试中遇到的问题以及我的解决办法,希望大家多提意见和建议,以便可以改善和进步。

1. 背景
有的时候在同一个页面中包含不同的 iframe,例如 page A 中包含 iframe B 和 C,而且需要在他们中间切换,例如 B -> C ->B。

2. 问题
这时候就会很头疼,因为 iframe 跟单独的 page 不同,在 web driver 里不能取到当前所在的 iframe ID,但 page 可以用他的 title 或者 url 分辨,而且需要先切换回主页面,再进入下一个 iframe。这样的话就需要在从 C 到 B 的时候记住 B 的 ID 或者在 case 中进行切换,也就是说在 iframe 这个类的外面还要有地方切换回去,但是 iframe ID 是这个 iframe 的属性,而在这个类的外面反复的调用就会让代码显得很凌乱,一旦这个页面进行了改动则其他很多方法或用例都会受到影响。举个例子先:

page_a.py
class PageA(BasePage):
    def __init__(self, driver):
        pass

class IFrameB(BasePage, driver):
    def __init__(self):
        self.driver = driver

    def button_b1(self):
        return self.driver.find_element_by_id("buttonb1")

    def button_b2(self):
        return self.driver.find_element_by_id("buttonb2")

class IFrameC(BasePage):
    def __init__(self):
        self.driver = driver

    def button_c(self):
        return self.driver.find_element_by_id("buttonc")

test.py
def test_page_and_iframe():
    driver = webdriver.Firefox()
    driver.get("http://www.pagea.com")
    driver.switch_to_frame("iframeb")
    iframe_b = IFrameB(driver)
    button_b1 = iframe_b.button_b1()
    button_b.click()
    driver.switch_to.default_content()
    driver.switch_to_frame("iframec")
    iframe_c = IFrameC(driver)
    button_c = iframe_c.button_c()
    driver.switch_to.default_content()
    driver.switch_to_frame("iframeb")
    iframe_b = IFrameB(driver)
    button_b2 = iframe_b.button_b2()

从上面的例子可以看到这个 iframe 以外还要记录需要切换的 iframe id,这样很不方便,而且代码变的很乱也不适用于维护。

3. 解决办法
其实自动化成功与否的关键,很大程度上是由他的维护成本决定的,所以我们要尽量减少由于页面或需求变更带来的改变。我们应该尽量把页面的信息保留在这个页面的类中,减少因为改变对于外部代码的破坏。首先我们需要在整个用例中维护一个当前 title 和 iframe id,然后我们定义一个装饰器, 这个装饰器的作用是检测当前的 title 和 iframe,如果和需要使用的类中定义的不一样,则切换并更新全局变量中的 title 和 iframe id:

def stay_window_frame(func):  
    def stay(cls):  
        if cls.env.driver.title != cls._title:  
            cls.env.driver.switch_to_window(cls._title)  
            if cls.env.current_frame == "None":  
                cls.env.current_frame = "default_content"  
        if cls.env.current_frame != cls._frame[-1]:  
            cls.env.driver.switch_to_default_content()  
            cls.env.current_frame = "default_content"  
            if cls._frame[-1] != "default_content":  
                for f in cls._frame:  
                    cls.env.driver.switch_to_frame(f)  
                cls.env.current_frame = cls._frame[-1]  
        ele = func(cls)  
        return ele  
    return stay

然后我们只需要在 page object 中指定他的 title 或 iframe id,并在使用 root element 的函数时使用这个装饰器。

page_a.py
class PageA(BasePage):
    def __init__(self, env):
        self.driver = env.driver
        self._title = ""

class IFrameB(BasePage, driver):
    def __init__(self, env):
        self.driver = env.driver
        self._iframe = ["iframeb"]

    @stay_window_frame
    def button_b1(self):
        return self.driver.find_element_by_id("buttonb1")

    @stay_window_frame
    def button_b2(self):
        return self.driver.find_element_by_id("buttonb2")

class IFrameC(BasePage):
    def __init__(self, env):
        self.driver = env.driver
        self._iframe = ["iframec"]

    @stay_window_frame
    def button_c(self):
        return self.driver.find_element_by_id("buttonc")

test.py
def test_page_and_iframe():
    driver = webdriver.Firefox()
    driver.get("http://www.pagea.com")
    iframe_b = IFrameB(driver)
    button_b1 = iframe_b.button_b1()
    button_b.click()
    iframe_c = IFrameC(driver)
    button_c = iframe_c.button_c()
    iframe_b = IFrameB(driver)
    button_b2 = iframe_b.button_b2()

这样可以看到,在 test 里面就变的清爽很多,而且即使 iframe 有任何变更,完全不用担心,只需要修改这个 iframe 所在的类就可以了。


↙↙↙阅读原文可查看相关链接,并与作者交流