Currently I have a function that creates a screenshot and I call it here
def tearDown(self):
self.save_screenshot()
self.driver.quit()
There is also a folder being created which is used to store the screenshots.
I don't want this to happen when the test passes.
What do I have to add in order for this not to happen?
Thanks for all the help
If your test failed, the sys.exc_info will have an exception. So you can use it as pass/fail result of your test:
if sys.exc_info()[0]:
# 'Test Failed'
else:
# 'Test Passed'
And if you want to take a screenshot on failure:
import unittest
import sys
from selenium import webdriver
class UrlTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
def test_correct_url(self):
self.driver.get('https://google.com')
self.assertTrue('something.com' in self.driver.current_url)
def tearDown(self):
if sys.exc_info()[0]:
self.driver.get_screenshot_as_file('screenshot.png')
self.driver.quit
if __name__ == '__main__':
unittest.main()
Here is one way to capture screenshot only when failure:
def setUp(self):
# Assume test will fail
self.test_failed = True
def tearDown(self):
if self.test_failed:
self.save_screenshot()
def test_something(self):
# do some tests
# Last line of the test:
self.test_failed = False
The rationale behind this approach is when the test reaches the last line, we know that the test passed (e.g. all the self.assert* passed). At this point, we reset the test_failed member, which was set to True in the setUp. In tearDown, we now can tell if a test passed or failed and take screenshot when appropriate.
In your initialisation method set a self.NoFailuresSnapped = 0 and check your test environment for the current number of failures being > self.NoFailuresSnapped before calling or within your function and of course set it again before returning.
Related
I am using sample code with unittest but I receive an error when I execute it -- 'str' object has no attribute 'get'.
I searched Google but did not get an answer.
from selenium import webdriver
import unittest
class google1search(unittest.TestCase):
driver = 'driver'
#classmethod
def setupClass(cls):
cls.driver = webdriver.Chrome(chrome_options=options)
cls.driver.implicitly_wait(10)
cls.driver.maximize_window()
def test_search_automationstepbystep(self):
self.driver.get("https://google.com")
self.driver.find_element_by_name("q").send_keys("Automation Step By step")
self.driver.find_element_by_name("btnk").click()
def test_search_naresh(self):
self.driver.get("https://google.com")
self.driver.find_element_by_name("q").send_keys("Naresh")
self.driver.find_element_by_name("btnk").click()
#classmethod
def teardownClass(cls):
cls.driver.close()
cls.driver.quit()
print("Test completed")
if __name__== "__main__":
unittest.main()
Supposed to execute 2 steps and give result pass.
In the above code :
There is no initialization of self.driver for
self.driver.get("https://google.com")
as the driver initiated is for
cls.driver = webdriver.Chrome(chrome_options=options)
please replace cls with self
I want to extend #sarthak response little bit. In the sample code methods setUpClass and tearDownClass are used. These methods are called to prepare test class and called only once before executing all test in your test class.
Which can work in your case because at the beginning of each test you're overwriting internal state of the driver object and your previous test execution shouldn't affect your next test result. In this case you need to modify your tests to use class object:
def test_search_automationstepbystep(self):
TestClass.driver.get("https://google.com")
TestClass.driver.find_element_by_name("q").send_keys("Automation Step By step")
TestClass.driver.find_element_by_name("btnk").click()
def test_search_naresh(self):
TestClass.driver.get("https://google.com")
TestClass.driver.find_element_by_name("q").send_keys("Naresh")
TestClass.driver.find_element_by_name("btnk").click()
Where TestClass is the name of your test class.
The other option is to use setUp and tearDown methods to initialize driver object before each test case:
def setUp(self):
self.driver = webdriver.Chrome(chrome_options=options)
self.driver.implicitly_wait(10)
self.driver.maximize_window()
def tearDown(self):
self.driver.close()
self.driver.quit()
print("Test completed")
Both setUp and tearDown methods accept instance of TestClass as self argument and your tests should work without any additional changes.
Note: Usually second option is preferable for unit testing, cause you don't need to make sure in every test that you overwrite driver internal state before using find_element_by_name. And in second option you can put self.driver.get("https://google.com") code into setUp method, cause it's going to be executed before each test case anyway.
I am somewhat of a beginner in python, i am currently writing a suite of test cases with selenium webdriver using unittest; i have also found a lot of useful answers here, but it's time a ask my first question, i have struggled a lot with this and cannot find a proper answer, so any help is greatly appreciated:
For short, i have a suite of multiple tests cases, and in each case the first step is always ".get('Some URL')"; i have written these test cases for a single environment, but i would like to be able to select the URL on which all tests will be executed. In the example below i called the "access_url" method with a specific environment, but i need to do this for all of my scenarios at once, is it possible to do this from where i execute the .py file (e.g. "python example.py")? or to pass it in the .run() method when i select what suite to run?
import HTMLTestRunner
from selenium import webdriver
import unittest
This is a custom class used to create the 'access_url' method
def MyClass(object):
def __init__(self, driver):
self.driver = driver
def access_url(self, URL):
if URL == 'environment 1':
self.driver.get('https://www.google.com/')
elif URL == 'environment 2':
self.driver.get('https://example.com/')
In the classes i use to write test cases the first step is always 'access URL'
class TestScenario01(unittest.TestCase):
def setUp(self):
[...]
def test_01_access(self):
MyClass(self.driver).access_url(URL='environment 2')
def test_02(self):
[...]
def test_03(self):
[...]
In order to run the tests i place them all in a suite and use .run() on them
tc_scenario01 = unittest.TestLoader().loadTestsFromTestCase(TestScenario01)
test_suite = unittest.TestSuite([tc_scenario01])
HTMLReporterCustom.HTMLTestRunner().run(test_suite)
Finally, in order to execute the script i type the follwoing line in CMD: 'python example_file.py
As i mentioned above, all i want to do is to be able to somehow pass the URL one time to all test cases that call the "access_url()" method. Thanks!
You can maintain environment properties in separate config file.
config.py
DEFAULT_ENVIRONMENT='environment1'
URL = {
'environment1': 'https://www.google.com/',
'environment2': 'https://example.com/'
}
Your Class,
from package import config
def MyClass(object):
def __init__(self, driver):
self.driver = driver
def access_url(self):
self.driver.get(config.URL[config.DEFAULT_ENVIRONMENT])
Then test class will be as expected,
class TestScenario01(unittest.TestCase):
def setUp(self):
[...]
def test_01_access(self):
MyClass(self.driver).access_url()
def test_02(self):
[...]
def test_03(self):
[...]
While running test you can change,
main.py
from package import config
config.DEFAULT_ENVIRONMENT = 'enviroment2'
tc_scenario01 = unittest.TestLoader().loadTestsFromTestCase(TestScenario01)
test_suite = unittest.TestSuite([tc_scenario01])
HTMLReporterCustom.HTMLTestRunner().run(test_suite)
You can also pass the environment name while running python main.py.
main.py
if __name__ == '__main__':
config.DEFAULT_ENVIRONMENT = sys.argv[1] if len(sys.argv) > 2 else 'dev'
tc_scenario01 = unittest.TestLoader().loadTestsFromTestCase(TestScenario01)
test_suite = unittest.TestSuite([tc_scenario01])
HTMLReporterCustom.HTMLTestRunner().run(test_suite)
Ok please look at my example code
My ConnectBase class:
class ConnectBase(unittest.TestCase):
def setUp(self):
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['deviceName'] = '4cfe4b59'
desired_caps['platformVersion'] = '5.0'
desired_caps['appPackage'] = 'com.xyz.bookshelf'
desired_caps['appActivity'] = 'com.xyz.bookshelf.MainActivity'
desired_caps['noReset'] = False
self.driver_android = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
self.driver_android.implicitly_wait(30)
And my main file with tests:
import unittest
from connect import ConnectBase
from main_screen import MainScreenCheck
class MainTests(ConnectBase, MainScreenCheck):
with open('report.txt', 'w') as f:
f.write("----------Bookshelf----------\n")
def test_bookshelf_tutorial(self):
self.addToFile("Test Tutorial")
self.driver_android.orientation = 'LANDSCAPE'
super(MainTests, self).logout_screen_check()
def test_bookshelf_2(self):
self.addToFile("Test 2")
super(MainTests, self).login_screen_check()
def test_bookshelf_3(self):
self.addToFile("Test 3")
super(MainTests, self).loading_screen_check()
def test_bookshelf_4(self):
self.addToFile("Test 4")
super(MainTests, self).list_check()
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(MainTests)
unittest.TextTestRunner(verbosity=2).run(suite)
I run script -> it is connecting
It starts "test_bookshelf_tutorial()"
Test passed and i would like to continue with "test_bookshelf_2()" but the app is restarting... and i have to go throught tutorial screen again...
The problem is that every unittest "def test_xyz(self)" application is restarting so I can't use unittest function that shows the passed test in report becouse, each test I must go through everything that I made in tests before
I created my way to make a test report -> I'm adding each test result to txt file... but I wonder if there is a possibility to turn off this app restarting and use normal unittest reports?
Or maybe there is another great way to do reports of automation tests?
Try to make an order to your test cases, some times test depend from each other
in the first step open the application and close it only in the last step:
class MainTests(ConnectBase, TestCase):
def step1(self):
#open the application
def step2(self):
...
def steps(self):
for name in sorted(dir(self)):
if name.startswith("step"):
yield name, getattr(self, name)
def test_steps(self):
for name, step in self.steps():
try:
step()
except Exception as e:
self.fail("{} failed ({}: {})".format(step, type(e), e)
I suggest that you use a Test framework like 'TESTNG' to define test priority to manage tests order but be sure that the first test is always executed to open the application ;)
I have 92 tests and I want to make sure that no silent errors occurred during the calls.
Unfortunately error handing in OpenGL is not quite good. I want to test if glGetError() returns other then GL_NO_ERROR It is enough if I test it once per TestCase. It would be better if I could add an assert after every test methods. (I don't want to add it manually in 92 methods)
I made an example snippet that shows a solution that is not acceptable since the assert is done in the tearDownClass(cls) method and tearDownClass should not do any testing logic.
How can I add an extra assert after my tests?
The lines with comments show what I wan't to achieve.
import struct
import unittest
import ModernGL
class TestCase(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.ctx = ModernGL.create_standalone_context()
#classmethod
def tearDownClass(cls):
error = cls.ctx.error # Store error in a variable
cls.ctx.release() # Then release the context
cls.assertEqual(error, 'GL_NO_ERROR') # Check if there were errors before the release
def test_1(self):
...
def test_2(self):
...
def test_3(self):
...
if __name__ == '__main__':
unittest.main()
NOTE:
cls.ctx.error is a property (glGetError() as a string) and the possible values are:
"GL_NO_ERROR"
"GL_INVALID_ENUM"
"GL_INVALID_VALUE"
"GL_INVALID_OPERATION"
"GL_INVALID_FRAMEBUFFER_OPERATION"
"GL_OUT_OF_MEMORY"
"GL_STACK_UNDERFLOW"
"GL_STACK_OVERFLOW"
"GL_UNKNOWN_ERROR"
You could do the test in the tearDown (as opposed to tearDownClass) method, as this is a regular instance method:
class TestCase(unittest.TestCase):
def setUp(self):
self.ctx = ModernGL.create_standalone_context()
def tearDown(self):
error = self.ctx.error # Store error in a variable
self.ctx.release() # Then release the context
self.assertEqual(error, 'GL_NO_ERROR') # Check if there were errors before the release
I want to create unittest test with two different set up and tearDown methon in same class with two different test.
each test will use its specific setUp and tearDown method in python unittest framework.
could anyone help me.
class processtestCase(unittest.TestCase):
print "start the process test cases "
def setUp1(self):
unittest.TestCase.setUp(self)
def test_test1(self):
"test Functinality"
def tearDown1(self):
unittest.TestCase.tearDown(self)
def setUp2(self):
unittest.TestCase.setUp2(self)
def test_test2(self):
"test Functinality"
def tearDown2(self):
unittest.TestCase.tearDown2(self) '
if __name__ == '__main__':
unittest.main()
In the question, you mention that you have two tests, each with its own setup and teardown. There are at least two ways to go:
You can either embed the setUp and tearDown code into each of the tests:
class FooTest(unittest.TestCase):
def test_0(self):
... # 1st setUp() code
try:
... # 1st test code
except:
... # 1st tearDown() code
raise
def test_1(self):
... # 2nd setUp() code
try:
... # 2nd test code
except:
... # 2nd tearDown() code
raise
Alternatively, you can split the class into two classes:
class FooTest0(unittest.TestCase):
#classmethod
def setUp(cls):
...
#classmethod
def tearDown(cls):
...
def test(self):
...
The first option has fewer classes, is shorter, and more straightforsard. The second option more cleanly decouples setting up the fixture, and cleaning it up, then the test code itself. It also future proofs adding more tests.
You should judge the tradeoffs based on your specific case, and your personal preferences.