Closing the webdriver instance automatically after the test failed - python

My English is very poor but I'll try my best to describe the problem I encountered.
I used selenium webdriver to test a web site and the language that I used to write my script is python.Because of this,I used Pyunit.
I know that if my test suite have no exceptions,the webdriver instance will be closed correctly,(by the way, I used chrome) however,once a exception was threw,the script will be shut down and I have to close chrome manually.
I wonder that how can I achieved that when a python process quits , any remaining open WebDriver instances will also be closed.
By the way, I used Page Object Design Pattern,and the code below is a part of my script:
class personalcenter(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.page = personalCenter(self.driver,"admin","123456")
def testAddWorkExp(self):
blahblahblah...
def tearDown(self):
self.page.quit()
self.driver.quit()
if __name__ == "__main__":
unittest.main()
I haved searched the solution of this problem for a long time ,but almost every answer is depended on java and junit or testNG.How can I deal with this issue with Pyunit?
Thanks for every answer.

From the tearDown() documentation:
Method called immediately after the test method has been called and
the result recorded. This is called even if the test method raised an
exception, so the implementation in subclasses may need to be
particularly careful about checking internal state. Any exception
raised by this method will be considered an error rather than a test
failure. This method will only be called if the setUp() succeeds,
regardless of the outcome of the test method. The default
implementation does nothing.
So, the only case where tearDown will not be called is when setUp fails.
Therefore I would simply catch the exception inside setUp, close the driver, and re-raise it:
def setUp(self):
self.driver = webdriver.Chrome()
try:
self.page = personalCenter(self.driver,"admin","123456")
except Exception:
self.driver.quit()
raise

Hope!! this will help you. you might need to close the driver with boolean expression as False
from selenium import webdriver
import time
class Firefoxweb(object):
def __init__(self):
print("this is to run the test")
def seltest(self):
driver=webdriver.Chrome(executable_path="C:\\Users\Admin\PycharmProjects\Drivers\chromedriver.exe")
driver.get("URL")
driver.find_element_by_xpath("//*[#id='btnSkip']").click()
driver.find_element_by_xpath("//*[#id='userInput']").send_keys("ABC")
driver.find_element_by_xpath("//*[#id='passwordInput']").send_keys("*******")
driver.find_element_by_xpath("//*[#id='BtnLogin']").click()
driver.close(False)
FF=Firefoxweb()
FF.seltest()

Related

What is the right approach to unittest this method in Python?

I have a scraping module on my application that uses Beautiful Soup and Selenium to get website info with this function:
def get_page(user: str) -> Optional[BeautifulSoup]:
"""Get a Beautiful Soup object that represents the user profile page in some website"""
try:
browser = webdriver.Chrome(options=options)
wait = WebDriverWait(browser, 10)
browser.get('https://somewebsite.com/' + user)
wait.until(EC.presence_of_element_located((By.TAG_NAME, 'article')))
except TimeoutException:
print("User hasn't been found. Try another user.")
return None
return BeautifulSoup(browser.page_source, 'lxml')
I need to test this function in two ways:
if it is getting a page (the success case);
and if it is printing the warning and returning None when it's not getting any page (the failure case).
I tried to test like this:
class ScrapeTests(unittest.TestCase):
def test_get_page_success(self):
"""
Test if get_page is getting a page
"""
self.assertEqual(isinstance(sc.get_page('myusername'), BeautifulSoup), True)
def test_get_page_not_found(self):
"""
Test if get_page returns False when looking for a user
that doesn't exist
"""
self.assertEqual(sc.get_page('iwçl9239jaçklsdjf'), None)
if __name__ == '__main__':
unittest.main()
Doing it like that makes the tests somewhat slower, as get_page itself is slow in the success case, and in the failure case, I'm forcing a timeout error looking for a non-existing user. I have the impression that my approach for testing these functions is not the right one. Probably the best way to test it is to fake a response, so get_page won't need to connect to the server and ask for anything.
So I have two questions:
Is this "fake web response" idea the right approach to test this function?
If so, how can I achieve it for that function? Do I need to rewrite the get_page function so it can be "testable"?
EDIT:
I tried to create a test to get_page like this:
class ScrapeTests(TestCase):
def setUp(self) -> None:
self.driver = mock.patch(
'scrape.webdriver.Chrome',
autospec=True
)
self.driver.page_source.return_value = "<html><head></head><body><article>Yes</article></body></html>"
self.driver.start()
def tearDown(self) -> None:
self.driver.stop()
def test_get_page_success(self):
"""
Test if get_page is getting a page
"""
self.assertEqual(isinstance(sc.get_page('whatever'), BeautifulSoup), True)
The problem I'm facing is that the driver.page_source attribute is created only after the wait.until function call. I need the wait.until because I need that Selenium browser waits for the javascript to create the article tags in HTML in order for me to scrape them.
When I try to define a return value for page source in setUp, I get an error: AttributeError: '_patch' object has no attribute 'page_source'
I tried lots of ways to mock webdriver attributes with mock. patch but it seems very difficult to my little knowledge. I think that maybe the best way to achieve what I desire (test get_page function without the need to connect to a server) is to mock an entire web server connection. But this is just a guess.
I think you are on the right track with #1. Look into the mock library which allows you to mock (fake) out functions, methods and classes and control the results of method calls. This will also remove the latency of the actual calls.
In my experience it is best to focus on testing the local logic and mock out any external dependencies. If you do that you will have a lot of small unit tests that together will test the majority of your code.
Based on your update, try:
self.driver = mock.patch_object(webdriver.Chrome, 'page_source', return_value="<html><head></head><body><article>Yes</article></body></html>"
If that doesn't work, then unfortunately I am out of ideas. I't possible the Selenium code is harder to mock.

How do you clean up resources at the end of a pytest session?

I have a suite of selenium end to end tests. I instantiate one webdriver.ChromeDriver instance that I pass as a fixture to every test in the suite.
'''python3
#pytest.fixture(scope="session")
def driver(request):
return d(request)
'''
After the last test runs I want to close the webdriver instance with driver.quit().
I can accomplish this with a fixture
#pytest.hookimpl(trylast=True)
def close_after_test_suite(driver, requests):
if requests:
driver.quit()
However this only runs if tests do not fail during the suite. If every test passes, the browser will close. If a single test fails then the browser will be left open.
How can I use pytest to close my browser at the end of the test suite regardless of whether or not tests fail?
If you want to run some code on teardown, use yield instead of return, for example:
#pytest.fixture(scope="session")
def driver(request):
driver = d(request)
yield driver
driver.quit()
However, the selenium.webdriver.WebDriver classes implement the context manager magic methods, so you can also use the with statement:
#pytest.fixture(scope="session")
def driver(request):
with d(request) as driver:
yield driver
Another (although pretty old, but still working) option is using the request.addfinalizer option:
#pytest.fixture(scope="session")
def driver(request):
driver = d(request)
def f():
driver.quit()
request.addfinalizer(f)
return driver

pytest exception none type object is not callable

In test1.py I have below code
#pytest.fixture(scope="session")
def moduleSetup(request):
module_setup = Module_Setup()
request.addfinalizer(module_setup.teardown())
return module_setup
def test_1(moduleSetup):
print moduleSetup
print '...'
#assert 0
# def test_2(moduleSetup):
# print moduleSetup
# print '...'
# #assert 0
And in conftest.py I have
class Module_Setup:
def __init__(self):
self.driver = webdriver.Firefox()
def teardown(self):
self.driver.close()
When I run it launches and closes browser.
But I also get error self = <CallInfo when='teardown' exception: 'NoneType' object is not callable>, func = <function <lambda> at 0x104580488>, when = 'teardown'
Also If I want to run both tests test_1 and test_2 with same driver object I need to use scope module or session?
Regarding the exception
When using request.addfinalizer(), you shall pass in reference to a function.
Your code is passing result of calling that function.
request.addfinalizer(module_setup.teardown())
You shall call it this way:
request.addfinalizer(module_setup.teardown)
Regarding fixture scope
If your fixture allows reuse across multiple test calls, use "session"
scope. If it allows reuse only for tests in one module, use "module" scope.
Alternative fixture solution
The way you use the fixtures is not much in pytest style, it rather resembles unittest.
From the code you show it seems, the only think you need is to have running Firefox with driver allowing to use it in your tests, and after being done, you need to close it.
This can be accomplished by single fixture:
#pytest.fixture(scope="session")
def firefox(request):
driver = webdriver.Firefox()
def fin():
driver.close()
request.addfinalizer(fin)
or even better using #pytest.yield_fixture
#pytest.yield_fixture(scope="session")
def firefox(request):
driver = webdriver.Firefox()
yield driver
driver.close()
The yield is place, where fixture stops executing, yields the created value (driver) to test cases.
After the tests are over (or better, when the scope of our fixture is over), it
continues running the instructions following the yield and does the cleanup
work.
In all cases, you may then modify your test cases as follows:
def test_1(firefox):
print moduleSetup
print '...'
and the moduleSetup fixture becomes completely obsolete.

selenium test not opening a browser django

I am trying to run a selenium test and this seems nothing happening. I tested the following code to make sure the setting was working: the firefox browser loaded as it is expected.
In functional_tests.py
browser = webdriver.Firefox()
broswer.get('http://localhost:8000')
But when I changed it to as follows:
import unittest
from selenium import webdriver
class NewVistorTest(unittest.TestCase):
def setUp(self):
self.browser = webdriver.Firefox()
def tearDown(self):
self.browser.quit()
def test_can_open_browser(self):
self.browser.get('http://localhost:8000')
self.assertIn('Test', self.browser.title)
It was not opening the browser, nothing was happening. I ran this python functional_tests.py
What is the best way to organize unit tests and selenium tests. I'd like to run it by module name, not all in tests.py or test_abc.py, not by nose.
How do you expect it to run? Python won't automatically run tests, just because they're in a class. You need to add a couple more lines to the end of your file:
class NewVistorTest(unittest.TestCase):
...
if __name__ == '__main__':
unittest.main(warnings='ignore')
This conditional is how a Python script checks if it has been executed from the command line, rather than just imported by another script. We call unittest.main() to launch the unittest test runner, which will automatically find test and methods in the file and run them.
Without that block, as you've seen, nothing happens.

Selenium - Results while using Python HTMLTestRunner/TextTestRunner within a loop

My apologies if this is not clear at any point (or if the vernacular isn't quite right) but I will do my best!
First time actually posting in a forum ;) ...
Any help from those in the know would be greatly appreciated.
I am using Selenium with Python bindings to conduct automated testing of a web based application across a number of platforms (OS) and browsers.
As part of this I am using the HTMLTestRunner module to generate a report at the end of each test run. It is here in that my problem lies.
The result of my code as it currently reads is that for each platform/ browser combination within respective lists, the HTMLTestRunner module is initialised and conducts a single test case... in turn generating the report and closing.
This creates problems with the generated report overwriting it self (or leads to formatting problems) as HTMLTestRunner is designed to be initialised, then conduct all test cases, then create a single report using all the test results.
Incidently if I use 'unittest.TextTestRunner()' instead of the HTMLTestRunner then essentially the same thing is happening only the reults are obviously displayed in the shell. ran 1 test... OK... ran 1 test... OK... et
I have tried using the line 'unittest.main(exit=False) which actually appears to work for results displyed within the shell eg all tests are run before any report is provided.
Unfortunatly I have not found a way of using this functionality with HTMLTestRunner.
(I suspect that someone is going to come back with using a meta class to dynamically generate tests instead of looping the execution code line like this. While I have looked into this I found myself at a complete loss as to how to implement this and a number of developer types have told me that this is something to steer clear of us (don't want to start a debate here!).)
Anyway a simplified example of the code that I am using is as per below:
global platform_list
platform_list = ['XP','VISTA','LINUX','WIN8','MAC']
global browser_list
browser_list = ['internet explorer','firefox','chrome','safari','opera']
class configuration(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Remote(command_executor= executor, desired_capabilities={'platform': platform, 'browserName': browser})
self.driver.implicitly_wait(30)
self.base_url = environment
self.verificationErrors = []
self.accept_next_alert = True
def is_element_present(self, how, what):
try: self.driver.find_element(by=how, value=what)
except NoSuchElementException, e: return False
return True
def is_alert_present(self):
try: self.driver.switch_to_alert()
except NoAlertPresentException, 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)
class test_case_A(configuration):
def test_case_A(self):
try:
some code
except:
some code
class test_case_B(test_case_A):
def test_case_B(self):
self.test_case_A()
try:
some code
except:
some code
unit_test_suite = unittest.TestSuite()
unit_test_suite.addTest(test_case_A('test_case_A')), (test_case_B('test_case_B'))
results = file('results.html', 'wb')
runner = HTMLTestRunner.HTMLTestRunner(stream = results, title = 'Whatever', description = 'Whatever')
global platform
global browser
for platform in platform_list:
for browser in browser_list:
unittest.TextTestRunner().run(unit_test_suite)
I just want to state that while certain elements of the above code may be unnessecary such as global declarations they are required in the actual!
Any help would be greatly appreciated! Many thanks...
I'm not sure if this answer will assist you as it may require a substantial restructure of your code, as far as I can understand.
For running my regression suite I have a particular module that builds the regression suite and then runs the HTMLtestrunner.
So each of my test-modules with multiple test-cases is imported, added to the TestSuite and then the suite itself is launched.
Here is a chopped down example:
from unittest import TestLoader, TestSuite
import HTMLTestRunner
import datetime
from root.nested.tests.test_class_1 import TestClass1
from root.nested.tests.test_class_2 import TestClass2
class RegressionSuite():
if __name__ == "__main__":
file_name = datetime.datetime.now().strftime("%Y_%m_%d_%H%M_report.html")
output = open(file_name, "wb")
loader = TestLoader()
suite = TestSuite((
loader.loadTestsFromTestCase(TestClass1),
loader.loadTestsFromTestCase(TestClass2)
))
runner = HTMLTestRunner(stream = output, verbosity = 1, title="Regression Suite")
runner.run(suite)
When this module is executed, the resulting HTML results file lists each test-class with a summary and the detail can be shown to display the result for each test-case as well.

Categories