Add the Deferred Assertions feature with rebranding

This commit is contained in:
Michael Mintz 2020-05-28 03:26:43 -04:00
parent 2cf0ba61c6
commit c3312267a3
4 changed files with 84 additions and 54 deletions

View File

@ -0,0 +1,22 @@
"""
This test demonstrates the use of deferred asserts.
Deferred asserts won't raise exceptions from failures until either
process_deferred_asserts() is called, or the test reaches the tearDown() step.
"""
import pytest
from seleniumbase import BaseCase
class MyTestClass(BaseCase):
@pytest.mark.expected_failure
def test_deferred_asserts(self):
self.open('https://xkcd.com/993/')
self.wait_for_element('#comic')
self.deferred_assert_element('img[alt="Brand Identity"]')
self.deferred_assert_element('img[alt="Rocket Ship"]') # Will Fail
self.deferred_assert_element('#comicmap')
self.deferred_assert_text('Fake Item', '#middleContainer') # Will Fail
self.deferred_assert_text('Random', '#middleContainer')
self.deferred_assert_element('a[name="Super Fake !!!"]') # Will Fail
self.process_deferred_asserts()

View File

@ -1,17 +0,0 @@
import pytest
from seleniumbase import BaseCase
class MyTestClass(BaseCase):
@pytest.mark.expected_failure
def test_delayed_asserts(self):
self.open('https://xkcd.com/993/')
self.wait_for_element('#comic')
self.delayed_assert_element('img[alt="Brand Identity"]')
self.delayed_assert_element('img[alt="Rocket Ship"]') # Will Fail
self.delayed_assert_element('#comicmap')
self.delayed_assert_text('Fake Item', '#middleContainer') # Will Fail
self.delayed_assert_text('Random', '#middleContainer')
self.delayed_assert_element('a[name="Super Fake !!!"]') # Will Fail
self.process_delayed_asserts()

View File

@ -472,11 +472,14 @@ self.check_window(name="default", level=0, baseline=False)
############
self.delayed_assert_element(selector, by=By.CSS_SELECTOR, timeout=None)
self.deferred_assert_element(selector, by=By.CSS_SELECTOR, timeout=None)
# Duplicates: self.delayed_assert_element(selector, by=By.CSS_SELECTOR, timeout=None)
self.delayed_assert_text(text, selector="html", by=By.CSS_SELECTOR, timeout=None)
self.deferred_assert_text(text, selector="html", by=By.CSS_SELECTOR, timeout=None)
# Duplicates: self.delayed_assert_text(text, selector="html", by=By.CSS_SELECTOR, timeout=None)
self.process_delayed_asserts()
self.process_deferred_asserts(print_only=False)
# Duplicates: self.process_delayed_asserts(print_only=False)
############

View File

@ -76,15 +76,15 @@ class BaseCase(unittest.TestCase):
self.driver = None
self.environment = None
self.env = None # Add a shortened version of self.environment
self.__last_url_of_delayed_assert = "data:,"
self.__last_url_of_deferred_assert = "data:,"
self.__last_page_load_url = "data:,"
self.__last_page_screenshot = None
self.__last_page_screenshot_png = None
self.__last_page_url = None
self.__last_page_source = None
self.__added_pytest_html_extra = None
self.__delayed_assert_count = 0
self.__delayed_assert_failures = []
self.__deferred_assert_count = 0
self.__deferred_assert_failures = []
self.__device_width = None
self.__device_height = None
self.__device_pixel_ratio = None
@ -4292,81 +4292,83 @@ class BaseCase(unittest.TestCase):
exc_message = update
return exc_message
def __add_delayed_assert_failure(self):
""" Add a delayed_assert failure into a list for future processing. """
def __add_deferred_assert_failure(self):
""" Add a deferred_assert failure to a list for future processing. """
current_url = self.driver.current_url
message = self.__get_exception_message()
self.__delayed_assert_failures.append(
self.__deferred_assert_failures.append(
"CHECK #%s: (%s)\n %s" % (
self.__delayed_assert_count, current_url, message))
self.__deferred_assert_count, current_url, message))
def delayed_assert_element(self, selector, by=By.CSS_SELECTOR,
timeout=None):
############
def deferred_assert_element(self, selector, by=By.CSS_SELECTOR,
timeout=None):
""" A non-terminating assertion for an element on a page.
Failures will be saved until the process_delayed_asserts()
Failures will be saved until the process_deferred_asserts()
method is called from inside a test, likely at the end of it. """
if not timeout:
timeout = settings.MINI_TIMEOUT
if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
self.__delayed_assert_count += 1
self.__deferred_assert_count += 1
try:
url = self.get_current_url()
if url == self.__last_url_of_delayed_assert:
if url == self.__last_url_of_deferred_assert:
timeout = 1
else:
self.__last_url_of_delayed_assert = url
self.__last_url_of_deferred_assert = url
except Exception:
pass
try:
self.wait_for_element_visible(selector, by=by, timeout=timeout)
return True
except Exception:
self.__add_delayed_assert_failure()
self.__add_deferred_assert_failure()
return False
def delayed_assert_text(self, text, selector="html", by=By.CSS_SELECTOR,
timeout=None):
def deferred_assert_text(self, text, selector="html", by=By.CSS_SELECTOR,
timeout=None):
""" A non-terminating assertion for text from an element on a page.
Failures will be saved until the process_delayed_asserts()
Failures will be saved until the process_deferred_asserts()
method is called from inside a test, likely at the end of it. """
if not timeout:
timeout = settings.MINI_TIMEOUT
if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
self.__delayed_assert_count += 1
self.__deferred_assert_count += 1
try:
url = self.get_current_url()
if url == self.__last_url_of_delayed_assert:
if url == self.__last_url_of_deferred_assert:
timeout = 1
else:
self.__last_url_of_delayed_assert = url
self.__last_url_of_deferred_assert = url
except Exception:
pass
try:
self.wait_for_text_visible(text, selector, by=by, timeout=timeout)
return True
except Exception:
self.__add_delayed_assert_failure()
self.__add_deferred_assert_failure()
return False
def process_delayed_asserts(self, print_only=False):
""" To be used with any test that uses delayed_asserts, which are
def process_deferred_asserts(self, print_only=False):
""" To be used with any test that uses deferred_asserts, which are
non-terminating verifications that only raise exceptions
after this method is called.
This is useful for pages with multiple elements to be checked when
you want to find as many bugs as possible in a single test run
before having all the exceptions get raised simultaneously.
Might be more useful if this method is called after processing all
the delayed asserts on a single html page so that the failure
screenshot matches the location of the delayed asserts.
the deferred asserts on a single html page so that the failure
screenshot matches the location of the deferred asserts.
If "print_only" is set to True, the exception won't get raised. """
if self.__delayed_assert_failures:
if self.__deferred_assert_failures:
exception_output = ''
exception_output += "\n*** DELAYED ASSERTION FAILURES FOR: "
exception_output += "\n*** DEFERRED ASSERTION FAILURES FROM: "
exception_output += "%s\n" % self.id()
all_failing_checks = self.__delayed_assert_failures
self.__delayed_assert_failures = []
all_failing_checks = self.__deferred_assert_failures
self.__deferred_assert_failures = []
for tb in all_failing_checks:
exception_output += "%s\n" % tb
if print_only:
@ -4376,6 +4378,26 @@ class BaseCase(unittest.TestCase):
############
# Alternate naming scheme for the "deferred_assert" methods.
def delayed_assert_element(self, selector, by=By.CSS_SELECTOR,
timeout=None):
""" Same as self.deferred_assert_element() """
return self.deferred_assert_element(
selector=selector, by=by, timeout=timeout)
def delayed_assert_text(self, text, selector="html", by=By.CSS_SELECTOR,
timeout=None):
""" Same as self.deferred_assert_text() """
return self.deferred_assert_text(
text=text, selector=selector, by=by, timeout=timeout)
def process_delayed_asserts(self, print_only=False):
""" Same as self.process_deferred_asserts() """
self.process_deferred_asserts(print_only=print_only)
############
def __js_click(self, selector, by=By.CSS_SELECTOR):
""" Clicks an element using pure JS. Does not use jQuery. """
selector, by = self.__recalculate_selector(selector, by)
@ -5127,15 +5149,15 @@ class BaseCase(unittest.TestCase):
"""
self.__slow_mode_pause_if_active()
has_exception = self.__has_exception()
if self.__delayed_assert_failures:
if self.__deferred_assert_failures:
print(
"\nWhen using self.delayed_assert_*() methods in your tests, "
"remember to call self.process_delayed_asserts() afterwards. "
"\nWhen using self.deferred_assert_*() methods in your tests, "
"remember to call self.process_deferred_asserts() afterwards. "
"Now calling in tearDown()...\nFailures Detected:")
if not has_exception:
self.process_delayed_asserts()
self.process_deferred_asserts()
else:
self.process_delayed_asserts(print_only=True)
self.process_deferred_asserts(print_only=True)
if self.is_pytest:
# pytest-specific code
test_id = self.__get_test_id()