为什么这个 Selenium 测试会失败,除非我插入一个 pdb 断点?
Why does this Selenium test fail UNLESS I insert a pdb breakpoint?
我有一个测试 AJAXy 页面的 Selenium LiveServerTestCase(django 项目)。主页加载后,在加载另一个项目之前有一段延迟,我需要测试第二个项目。我正在尝试包含一种不执行 time.sleep(too_long)
、 等待的智能方法,但测试总是失败,除非我插入 pdb 断点。
def some_test_thing(self):
#loads a page with some ajaxy stuff, so there's a
#delay that needs to be accounted for
url = "something..."
self.browser.get(url)
#import ipdb;ipdb.set_trace() #this test only passes with this statement, wtf^^?
self.assertWithWait(self.assert_, args=(some_args,))
我使用便利函数 assertWithWait,它在循环中尝试断言,如果测试失败则捕获 AssertionError,直到超时。
def assertWithWait(self, assertion, timeout=10, args=(), kwargs={}):
total_time = 0
while total_time < timeout:
try:
assertion(*args, **kwargs)
except AssertionError:
pass
else: # assertion passed
return
time.sleep(0.2)
total_time += 0.2
assertion(*args, **kwargs) # final try, will cause test failure if unsuccessful
问题是,如果我没有那个 pdb 断点,测试总是会失败 - 循环一直运行到 10 秒超时。但是,如果我包含该 pdb 断点 ,即使我输入 "c" 并立即继续(比如在 1 秒内),测试也会通过 。很明显这不是时间问题,因为测试在断点情况下的 10 秒超时内成功完成,但在无断点情况下等待 10 秒后失败。所以看起来 Selenium 正在做的与多处理或多线程相关的事情,可能是由 pdb 断点释放的?我在这里抓住了一些稻草。
求助!
编辑
我使用这个 assertWithWait 不仅是为了等待页面中的内容加载,而且是为了等待某些 AJAXy 服务器调用的结果,以检查数据库中的内容是否也发生了变化。所以我不能只使用 Selenium 的内置 WebDriverWait 函数。对不起,应该更清楚这一点。
你基本上是在重新发明Explicit Wait的用途,使用它并处理TimeoutException
:
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def some_test_thing(self):
try:
element = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "myDynamicElement"))
)
except TimeoutException:
self.fail("Element not found")
This waits up to 10 seconds before throwing a TimeoutException or if
it finds the element will return it in 0 - 10 seconds. WebDriverWait
by default calls the ExpectedCondition every 500 milliseconds until it
returns successfully. A successful return is for ExpectedCondition
type is Boolean return true or not null return value for all other
ExpectedCondition types.
DUH,我的错。对象!让我们看一下 assertWithWait 的调用签名以及稍后执行断言的方式,问题就会变得清晰起来。所以这是相关的东西:
def assertWithWait(self, assertion, timeout=10, args=(), kwargs={}):
...
assertion(*args, **kwargs)
...
假设断言是
self.assertEqual(SomeModel.objects.get(pk=1).some_field,
"some value")
所以我们这样称呼assertWithWait
:
self.assertWithWait(self.assertEqual,
args=(SomeModel.objects.get(pk=1).some_field,
"some value"))
你能发现我的愚蠢错误吗??
在调用 assertWithWait 时正在评估 args,因此在 assertWithWait 内循环的每次迭代中,使用完全相同的参数(对象!)调用 assertEqual .
修复方法是将 assertWithWait 的调用签名更改为使用 lambda:
def assertWithWait(self, assertion_func, timeout=6):
"""
Tries the assertion in a loop until a timeout.
Usage:
self.assertWithWait(lambda: assertion_statement)
e.g.
self.assertWithWait(lambda: self.assertEqual(something, something_else))
"""
...
try:
assertion_func()
except AssertionError:
pass
...
现在我们有了一个通用的 assertWithWait,它可以检查浏览器元素或数据库值(或您喜欢的任何其他内容)。作为奖励,调用语法变得更易于阅读 - python ftw!
我有一个测试 AJAXy 页面的 Selenium LiveServerTestCase(django 项目)。主页加载后,在加载另一个项目之前有一段延迟,我需要测试第二个项目。我正在尝试包含一种不执行 time.sleep(too_long)
、 等待的智能方法,但测试总是失败,除非我插入 pdb 断点。
def some_test_thing(self):
#loads a page with some ajaxy stuff, so there's a
#delay that needs to be accounted for
url = "something..."
self.browser.get(url)
#import ipdb;ipdb.set_trace() #this test only passes with this statement, wtf^^?
self.assertWithWait(self.assert_, args=(some_args,))
我使用便利函数 assertWithWait,它在循环中尝试断言,如果测试失败则捕获 AssertionError,直到超时。
def assertWithWait(self, assertion, timeout=10, args=(), kwargs={}):
total_time = 0
while total_time < timeout:
try:
assertion(*args, **kwargs)
except AssertionError:
pass
else: # assertion passed
return
time.sleep(0.2)
total_time += 0.2
assertion(*args, **kwargs) # final try, will cause test failure if unsuccessful
问题是,如果我没有那个 pdb 断点,测试总是会失败 - 循环一直运行到 10 秒超时。但是,如果我包含该 pdb 断点 ,即使我输入 "c" 并立即继续(比如在 1 秒内),测试也会通过 。很明显这不是时间问题,因为测试在断点情况下的 10 秒超时内成功完成,但在无断点情况下等待 10 秒后失败。所以看起来 Selenium 正在做的与多处理或多线程相关的事情,可能是由 pdb 断点释放的?我在这里抓住了一些稻草。
求助!
编辑 我使用这个 assertWithWait 不仅是为了等待页面中的内容加载,而且是为了等待某些 AJAXy 服务器调用的结果,以检查数据库中的内容是否也发生了变化。所以我不能只使用 Selenium 的内置 WebDriverWait 函数。对不起,应该更清楚这一点。
你基本上是在重新发明Explicit Wait的用途,使用它并处理TimeoutException
:
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def some_test_thing(self):
try:
element = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "myDynamicElement"))
)
except TimeoutException:
self.fail("Element not found")
This waits up to 10 seconds before throwing a TimeoutException or if it finds the element will return it in 0 - 10 seconds. WebDriverWait by default calls the ExpectedCondition every 500 milliseconds until it returns successfully. A successful return is for ExpectedCondition type is Boolean return true or not null return value for all other ExpectedCondition types.
DUH,我的错。对象!让我们看一下 assertWithWait 的调用签名以及稍后执行断言的方式,问题就会变得清晰起来。所以这是相关的东西:
def assertWithWait(self, assertion, timeout=10, args=(), kwargs={}):
...
assertion(*args, **kwargs)
...
假设断言是
self.assertEqual(SomeModel.objects.get(pk=1).some_field,
"some value")
所以我们这样称呼assertWithWait
:
self.assertWithWait(self.assertEqual,
args=(SomeModel.objects.get(pk=1).some_field,
"some value"))
你能发现我的愚蠢错误吗??
在调用 assertWithWait 时正在评估 args,因此在 assertWithWait 内循环的每次迭代中,使用完全相同的参数(对象!)调用 assertEqual .
修复方法是将 assertWithWait 的调用签名更改为使用 lambda:
def assertWithWait(self, assertion_func, timeout=6):
"""
Tries the assertion in a loop until a timeout.
Usage:
self.assertWithWait(lambda: assertion_statement)
e.g.
self.assertWithWait(lambda: self.assertEqual(something, something_else))
"""
...
try:
assertion_func()
except AssertionError:
pass
...
现在我们有了一个通用的 assertWithWait,它可以检查浏览器元素或数据库值(或您喜欢的任何其他内容)。作为奖励,调用语法变得更易于阅读 - python ftw!