repetitive global variable and try except in python unit tests - python

I have started using Python Selenium and wrote a script shown below.
This prints a return code linked to the test that fails (test01, test02, test03, ..).
Ignore that each test is checking the same thing.
I'm just trying to understand if there's a cleaner way to write the tests because each one repetitively declares global res, and then has a try/except block.
Could anyone offer some advice on how to improve this please?
# global variable for return code. Zero is success.
res=0
#atexit.register
def send_health():
print ("res=%s") % res
class Login(unittest2.TestCase):
#classmethod
def setUpClass(inst):
binary = FirefoxBinary('C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe')
inst.driver = webdriver.Firefox(firefox_binary=binary)
inst.base_url = "https://stackoverflow.com"
def test01(self):
global res
driver = self.driver
try:
self.assertEqual("Ask a Question", driver.title)
except Exception,e:
print ("Exception: %s" % e)
driver.save_screenshot('screenshot_test01.png')
res=1
return
def test02(self):
global res
driver = self.driver
try:
self.assertEqual("Ask a Question", driver.title)
except Exception,e:
print ("Exception: %s" % e)
driver.save_screenshot('screenshot_test02.png')
res=2
return
def test03(self):
global res
driver = self.driver
try:
self.assertEqual("Ask a Question", driver.title)
except Exception,e:
print ("Exception: %s" % e)
driver.save_screenshot('screenshot_test03.png')
res=3
return
if __name__ == "__main__":
unittest2.main()

There is no need for global variables here at all. You are inside a class; use self.res throughout.

That's exactly what setUp instance method is for, quite similar to setUpClass method that is run once per test class.
def setUp(self):
# this code will be executed before each and every test
self.res = 0
def tearDown(self):
# this code will be executed post each and every test.
By the way, why are you using global variables? There's no need for them. In fact, there's seldom a valid rationale for using globals. Moreover, tests need to be isolated and independent, using global variables would violate that rule.

Thanks for the tips above. I've reduced the code down to this version which hopefully looks cleaner and more standard:
class Login(unittest2.TestCase):
#classmethod
def handleError(self, e, res):
print ("Test failed with exception: %s" % e)
self.result = res
sys.exit()
#classmethod
def setUpClass(self):
binary = FirefoxBinary('C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe')
self.driver = webdriver.Firefox(firefox_binary=binary)
self.base_url = "https://stackoverflow.com"
self.result = 0
def test01(self):
driver = self.driver
try:
self.assertEqual("Ask a Question", driver.title)
except Exception,e:
self.handleError(e, 1)
def test02(self):
driver = self.driver
try:
self.assertEqual("Ask a Question", driver.title)
except Exception,e:
self.handleError(e, 2)
def test03(self):
driver = self.driver
try:
self.assertEqual("Ask a Question", driver.title)
except Exception,e:
self.handleError(e, 3)
#classmethod
def tearDownClass(self):
self.driver.quit()
print ("res=%s") % self.result
if __name__ == "__main__":
unittest2.main()

Related

Using classes inside if statement

I have this kind of code
class disable_file_system_redirection:
if mysystem == "Windows":
_disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection
_revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection
def __enter__(self):
self.old_value = ctypes.c_long()
self.success = self._disable(ctypes.byref(self.old_value))
def __exit__(self, type, value, traceback):
if self.success:
self._revert(self.old_value)
else:
pass
If test == “yes”:
with disable_file_system_redirection:
try:
“some code”
else:
try:
“same code”
As you can see I wrote the same code twice. I cannot merge those two same codes without getting errors. Is there a possible way to do something like that
If test = = “yes”:
with disable_file_system_redirection:
else:
pass #without disable_file_system_redirection:
“some code”
you can outsource your code into a function:
def code_to_do():
print("code_to_do")
if test == "yes":
with disable_file_system_redirection:
try:
code_to_do()
except Exception as e:
print(str(e))
else:
try:
code_to_do()
except Exception as e:
print(str(e))

Using arg parser in python in another class

I'm trying to write a test in Selenium using python,
I managed to run the test and it passed, But now I want add arg parser so I can give the test a different URL as an argument.
The thing is that my test is inside a class,
So when I'm passing the argument I get an error:
app_url= (args['base_url'])
NameError: global name 'args' is not defined
How can I get args to be defined inside the Selenium class?
This is my code:
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
from selenium import webdriver
import unittest, time, re
import os
import string
import random
import argparse
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8))
agmuser = id_generator()
class Selenium(unittest.TestCase):
def setUp(self):
chromedriver = "c:\chromedriver.exe"
os.environ["webdriver.chrome.driver"] = chromedriver
self.driver = webdriver.Chrome(chromedriver)
app_url = (args['base_url'])
#app_url= "http://myd-vm16635.fufu.net:8080/"
print "this is the APP URL:" + ' ' + app_url
self.base_url = app_url
self.verificationErrors = []
self.accept_next_alert = True
def test_selenium(self):
#id_generator.user = id_generator()
driver = self.driver
driver.get(self.base_url + "portal/")
driver.find_element_by_css_selector("span").click()
driver.find_element_by_id("j_loginName").clear()
driver.find_element_by_id("j_loginName").send_keys(agmuser)
driver.find_element_by_id("btnSubmit").click()
driver.find_element_by_link_text("Login as" + ' ' + agmuser).click()
driver.find_element_by_css_selector("#mock-portal-Horizon > span").click()
# driver.find_element_by_id("gwt-debug-new-features-cancel-button").click()
# driver.find_element_by_xpath("//table[#id='gwt-debug-module-dropdown']/tbody/tr[2]/td[2]").click()
# driver.find_element_by_id("gwt-debug-menu-item-release-management").click()
def is_element_present(self, how, what):
try: self.driver.find_element(by=how, value=what)
except NoSuchElementException as e: return False
return True
def is_alert_present(self):
try: self.driver.switch_to_alert()
except NoAlertPresentException as e: return False
return True
def close_alert_and_get_its_text(self):
try:
alert = self.driver.switch_to_alert()
alert_text = alert.text
if self.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally: self.accept_next_alert = True
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
if __name__ == "__main__":
#####################******SCRIPT PARAMS****;**###################################
# these values can be changed type 'python selenium_job.py --help' for assistance
##################################################################################
parser = argparse.ArgumentParser(description='DevOps team - Sanity test')
parser.add_argument('-b', '--base_url', help='base_url', default="http://myd-vm16635.fufu.net:8080/")
args = vars(parser.parse_args())
unittest.main()
Put the parser = argparse.ArgumentParser(...) and parser.add_argument() outside if __name__ == "__main__": so that it always gets created but not evaluated. Keep args = vars(parser.parse_args()) inside __main__.
That way you can import it from the file like from selenium_tests import parser and then in your other script, do parser.parse_args().
And a cleaner way to do it is to create a function which returns the parser, like:
def get_parsed_args():
parser = argparse.ArgumentParser(...)
parser.add_argument(...)
# etc.
args = parser.parse_args()
return args
# or just...
return parser.parse_args()
#and then call that in the main program:
if __name__ == '__main__':
args = get_parsed_args()
# etc.
And in other scripts which you want to import it into, do
from selenium_tests import get_parsed_args
if __name__ == '__main__':
args = get_parsed_args()
# etc.

Python Selenium Test Suite Single Webdriver Instance?

I'm having some real issues trying to work out how to get this to work, and I'm sure there are some experts on here who can work it out for me, please :)
So I have a number of test cases in python that all follow on from each other, but are individual scripts, I want to combine these and run them in order, in a single webdriver instance, as they all follow on, but I can't seem to work out how to do it..
I have created a test suite -
import unittest
from Searchfieldreturnscorrectvalue import SearchFieldReturnsCorrectValue
from Navigatetostreetlightprecontentpage import Navigatetostreetlightprecontentpage
class TestSuite(unittest.TestSuite):
def suite():
suite = unittest.TestSuite()
suite.addTest(Searchfieldreturnscorrectvalue('test_searchfieldreturnscorrectvalue'))
suite.addTest(Navigatetostreetlightprecontentpage('test_navigatetostreetlightprecontentpage'))
return suite
if __name__ == "__main__":
unittest.main()
This runs the tests, but the second one fails as it tried to run it in a second firefox instance..
Searchfieldreturnscorrectvalue.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
class SearchFieldReturnsCorrectValue(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
self.base_url = "https://www.XXXXX.com/"
self.verificationErrors = []
self.accept_next_alert = True
def test_search_field_returns_correct_value(self):
driver = self.driver
driver.get(self.base_url + "/")
driver.find_element_by_id("edit-search-block-form--2").click()
driver.find_element_by_id("edit-query").clear()
driver.find_element_by_id("edit-query").send_keys("street light")
driver.find_element_by_id("edit-query").send_keys(Keys.ENTER)
for i in range(60):
try:
if self.is_element_present(By.LINK_TEXT, "Street lighting"): break
except: pass
time.sleep(1)
else: self.fail("time out")
try: self.assertEqual("Street lighting", driver.find_element_by_link_text("Street lighting").text)
except AssertionError as e: self.verificationErrors.append(str(e))
def is_element_present(self, how, what):
try: self.driver.find_element(by=how, value=what)
except NoSuchElementException as e: return False
return True
def is_alert_present(self):
try: self.driver.switch_to_alert()
except NoAlertPresentException as e: return False
return True
def close_alert_and_get_its_text(self):
try:
alert = self.driver.switch_to_alert()
alert_text = alert.text
if self.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally: self.accept_next_alert = True
def tearDown(self):
self.assertEqual([], self.verificationErrors)
if __name__ == "__main__":
unittest.main()
Navigatetostreetlightprecontentpage.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
class Navigatetostreetlightprecontentpage(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.verificationErrors = []
self.accept_next_alert = True
def test_navigatetostreetlightprecontentpage(self):
driver = self.driver
driver.find_element_by_link_text("Street lighting").click()
try: self.assertEqual("Street lighting", driver.find_element_by_css_selector("h1.page-title__main__title").text)
except AssertionError as e: self.verificationErrors.append(str(e))
try: self.assertEqual("Report a faulty street light | Cheshire East", driver.title)
except AssertionError as e: self.verificationErrors.append(str(e))
def is_element_present(self, how, what):
try: self.driver.find_element(by=how, value=what)
except NoSuchElementException as e: return False
return True
def is_alert_present(self):
try: self.driver.switch_to_alert()
except NoAlertPresentException as e: return False
return True
def close_alert_and_get_its_text(self):
try:
alert = self.driver.switch_to_alert()
alert_text = alert.text
if self.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally: self.accept_next_alert = True
def tearDown(self):
self.assertEqual([], self.verificationErrors)
if __name__ == "__main__":
unittest.main()
I don't know if a test suite is the correct way to do it, or to just get all the tests into one file, but I still would want the "Classes/Tests" to individually report pass/fail, at the moment I can't get that to work, I think it is something to do with the setUp(self) needing to be moved to a setUpModule and shared? But I can't work it out, if someone could please point me in the right direction, I would be very grateful.
Thanks
Update
Example of what I have tired as per comment bellow, still not working..
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
class SeleniumTest(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.driver = webdriver.Firefox()
cls.driver.maximize_window()
#classmethod
def tearDownClass(cls):
cls.driver.close()
cls.driver.quit()
class SearchFieldReturnsCorrectValue(SeleniumTest):
def setUp(cls):
cls.base_url = "https://www.XXXXX.com"
cls.verificationErrors = []
cls.accept_next_alert = True
def test_search_field_returns_correct_value(cls):
driver = cls.driver
driver.get(cls.base_url + "/")
driver.find_element_by_id("edit-search-block-form--2").click()
driver.find_element_by_id("edit-query").clear()
driver.find_element_by_id("edit-query").send_keys("street light")
driver.find_element_by_id("edit-query").send_keys(Keys.ENTER)
for i in range(60):
try:
if cls.is_element_present(By.LINK_TEXT, "Street lighting"): break
except: pass
time.sleep(1)
else: cls.fail("time out")
try: cls.assertEqual("Street lighting", driver.find_element_by_link_text("Street lighting").text)
except AssertionError as e: cls.verificationErrors.append(str(e))
driver.find_element_by_link_text("Street lighting").click()
def is_element_present(cls, how, what):
try: cls.driver.find_element(by=how, value=what)
except NoSuchElementException as e: return False
return True
def is_alert_present(cls):
try: cls.driver.switch_to_alert()
except NoAlertPresentException as e: return False
return True
def close_alert_and_get_its_text(cls):
try:
alert = cls.driver.switch_to_alert()
alert_text = alert.text
if cls.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally: cls.accept_next_alert = True
def tearDown(cls):
cls.assertEqual([], cls.verificationErrors)
class Navigatetostreetlightprecontentpage(SeleniumTest):
def setUp(cls):
cls.verificationErrors = []
cls.accept_next_alert = True
def test_navigatetostreetlightprecontentpage(cls):
driver = cls.driver
try: cls.assertEqual("Street lighting", driver.find_element_by_css_selector("h1.page-title__main__title").text)
except AssertionError as e: cls.verificationErrors.append(str(e))
try: cls.assertEqual("Report a faulty street light | Cheshire East", driver.title)
except AssertionError as e: cls.verificationErrors.append(str(e))
def is_element_present(cls, how, what):
try: cls.driver.find_element(by=how, value=what)
except NoSuchElementException as e: return False
return True
def is_alert_present(cls):
try: cls.driver.switch_to_alert()
except NoAlertPresentException as e: return False
return True
def close_alert_and_get_its_text(cls):
try:
alert = cls.driver.switch_to_alert()
alert_text = alert.text
if cls.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally: cls.accept_next_alert = True
def tearDown(cls):
cls.assertEqual([], cls.verificationErrors)
if __name__ == "__main__":
unittest.main()
This seems to be running both classes now, but the second class is never able to locate any elements, but the same line in the first class works perfectly.
I am not sure I understood well, but to use a single driver instance, you can use the setupClass class method where you create the driver:
class MyTestClass(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.driver = webdriver.Firefox()
#classmethod
def tearDownClass(cls):
cls.driver.close()
cls.driver.quit()
def setUp(self):
....
It will still recreate the driver for every new test class, but it does not recreate one for every test (as setUp does).
Personnally I make all my test classes inherit from a SeleniumTest class like this:
class SeleniumTest(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.driver = webdriver.Firefox()
#classmethod
def tearDownClass(cls):
cls.driver.close()
cls.driver.quit()
class MyTestClass(SeleniumTest):
def setUp(self):
....
import unittest
from selenium import webdriver
from time import sleep
class SeleniumTest(unittest.TestCase):
global driver
#classmethod
def setUpClass(cls):
#getting a common webdriver instance for all your tests for this module
cls.driver = webdriver.Chrome("/Users/sibasish/PycharmProjects/CommonDriverInstance/chromedriver")
cls.driver.get("https://www.google.com")
#classmethod
def tearDownClass(cls):
cls.driver.close()
cls.driver.quit()
class MyTestClass(SeleniumTest):
def setUp(self):
pass
def test_sample1(self):
print("hello1")
self.driver.get("https://www.google.com/")
sleep(4)
def test_sample2(self):
print("hello2")
self.driver.get("https://www.facebook.com/")
sleep(4)

Checking if HTML element exists with Python Selenium

I am trying to check if an element exists on an HTML page with Selenium/Python.
This is my function:
class runSelenium(object):
def __init__(self):
# define a class attribute
self.driver = webdriver.Firefox()
def isElementPresent(self, locator):
try:
self.driver.find_element_by_xpath(locator)
except NoSuchElementException:
print ('No such thing')
return False
return True
def selenium(self):
self.driver.get("https://somepage.com")
isElement = self.isElementPresent("//li[#class='item'][6]")
isElement1 = str(isElement)
if __name__ == '__main__':
run = runSelenium()
run.selenium()
I am trying to pick the result with a Boolean value but with no luck:
isElement = self.isElementPresent("//li[#class='item'][6]")
What am I missing here?
You need to un-indent the last code block:
class runSelenium(object):
def __init__(self):
# define a class attribute
self.driver = webdriver.Firefox()
def isElementPresent(self, locator):
try:
self.driver.find_element_by_xpath(locator)
except NoSuchElementException:
print ('No such thing')
return False
return True
def selenium(self):
self.driver.get("https://somepage.com")
isElement = self.isElementPresent("//li[#class='item'][6]")
isElement1 = str(isElement)
if __name__ == '__main__':
run = runSelenium()
run.selenium()
So I restarted the IDE (Visual Studio 2013) and it works now fine.. The code is correct 100%

Python try / except keep trying until no errors

I have the following code that occasionally crashes due to a permissions bug. I am trying to wrap it up in a try / except statement that will keep trying to launch the driver until successful...
def init_driver():
ffprofile = webdriver.FirefoxProfile("my_profile")
ffprofile.add_extension(extension="myaddon.xpi")
return driver
driver = init_driver()
I have seen examples letting me print a message if an error occurs but how do I get it to keep retrying? Does anybody have an example they can point me at?
Here's a loop that iterates over attempts:
while True:
try:
driver = init_driver()
break
except Foo:
continue
Note that this is not a bare except clause. Bare excepts are dangerous because they can capture things like NameError that are so rarely meant to be caught. You should put the specific exception you expect to catch here.
Here's one way to do it if you don't want to use a loop. Just recall the function on the exception
import sys
def init_driver(tries=0):
try:
ffprofile = webdriver.FirefoxProfile("my_profile");
ffprofile.add_extension(extension="myaddon.xpi")
return driver
except Exception: #This should be the exception you expect and not a catch all
if tries < sys.getrecursionlimit(): #By default 1,000 can be bumped up by setrecursionlimit
return init_driver(tries+1)
#just for kicks
#else:
#sys.setrecursionlimit(sys.getrecursionlimit() + 1)
#print("Yes we'll win this game the old-fashioned way, the tried and true way:")
#print("We'll cheat!")
#refactor / prettify if's to call init_driver if you want to cheat.
else:
print("OH NO RECURSION LIMIT HIT!!!!!! (ノಠ益ಠ)ノ彡┻━┻")
driver = init_driver()
Other answers are fine but they will keep retrying until it hits the recursion depth limit. Consider adding a retry limit:
def init_driver(retry_limit=10, nretry=0):
if nretry >= retry_limit:
return # retry limit reached, maybe raise an exception?
try:
ffprofile = webdriver.FirefoxProfile("my_profile");
ffprofile.add_extension(extension="myaddon.xpi")
except SomeException:
return init_driver(nretry=nretry+1)
return ffprofile
driver = init_driver()
Do like this:
def init_driver():
driver = None
ffprofile = webdriver.FirefoxProfile("my_profile");
ffprofile.add_extension(extension="myaddon.xpi")
# do something with a valid profile and set driver to something other than None
return driver
driver = None
while driver is None:
driver = init_driver()
Here is a recursive solution (with keeping track of the retries):
def init_driver(retries=0):
try:
ffprofile = webdriver.FirefoxProfile("my_profile");
ffprofile.add_extension(extension="myaddon.xpi")
except:
print('attempt nr. ' + str(retries))
return init_driver(retries+1)
return driver
Just a small change required. Where Foo is the specific exception you get with the permissions bug.
def init_driver():
try:
ffprofile = webdriver.FirefoxProfile("my_profile");
ffprofile.add_extension(extension="myaddon.xpi")
return driver
except Foo:
return init_driver()
driver = init_driver()

Categories