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 所在的类就可以了。