How I can define custom function and then use it in test function, it works when I run single test case but don't work when I run multiple test cases.
class AlphaTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
self.driver.get("http://google.com/")
def asserTrueId(self, value):
self.assertTrue(self.driver.find_element_by_id(value))
time.sleep(1)
def test_flush_cache(self):
self.asserTrueId("block-menu-menu-menu-for-directories")
You could use unittest.setUpClass() to instantiate a class level driver instance. Similarly you can use tearDownClass() to clean up class level variables if required.
Since setUpClass() and tearDownClass() will only be run once inside your Test class you can use this to guarantee there is only one driver. Otherwise using setUp() would be run for each test method - each creating a browser instance each time - which could be slow and possibly memory inefficient.
class AlphaTest(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.driver = webdriver.Firefox()
cls.driver.implicitly_wait(30)
cls.driver.get("http://google.com/")
def asserTrueId(self, value):
self.assertTrue(self.driver.find_element_by_id(value))
time.sleep(1)
def test_flush_cache(self):
self.asserTrueId("block-menu-menu-menu-for-directories")
Related
I've been looking for an answer but cant find one
Let's say I have this,
self.driver = webdriver.Chrome(options=self.options)
and then I use,
self.driver.get('http://www.google.com/')
Is there a way for me to use just 'self.' on the next method where everything from self.driver pops up? Like so,
self.get('http://www.google.com/')
Create get-method for your class that calls driver.get. Then you can refer to it inside the class as self.get.
class CustomDriver:
def __init__(self, driver):
self.driver = driver
def get(self, url):
self.driver.get(url)
def another_method(self):
self.get("<some_url>")
# do some other stuff here...
# initiate driver first
driver = ...
drv = CustomDriver(driver)
drv.get("<some_url>")
self in Python class
self represents the instance of the class. By using the self keyword
we can access the attributes and methods of the class in python. It
binds the attributes with the given arguments.
In short, self is always pointing to current object.
But get() is method from the WebDriver implementation. So to access get() you have to invoke it through the instance of the WebDriver i.e. driver which you have already initialized through:
self.driver = webdriver.Chrome(options=self.options)
I need to create a class that uses a fixture from conftest.py, and this class has a fixture that I need to use only once per test session.
I have two test classes that depend on this class that has a fixture.
Sample code in test_app.py:
#pytest.mark.usefixtures("driver_get")
class TestBase:
#pytest.fixture(scope="module", autouse=True)
def set_up(self):
# set up code.
# web page will load if this fixture is called
# uses self.driver, where driver was set in driver_get
class TestOne(TestBase):
def test_1(self):
# test code
# uses self.driver also in the test
class TestTwo(TestBase):
def test_2(self):
# test code
# uses self.driver also in the test
Sample code in conftest.py (follows https://dzone.com/articles/improve-your-selenium-webdriver-tests-with-pytest):
#pytest.fixture(scope="session")
def driver_get(request):
from selenium import webdriver
web_driver = webdriver.Chrome()
session = request.node
for item in session.items:
cls = item.getparent(pytest.Class)
setattr(cls.obj,"driver",web_driver)
yield
web_driver.close()
As you can see, conftest.py sets the driver as a class attribute, which is why I am applying the driver_get fixture to class TestBase, since I need to use the driver inside the class.
The problem is, once TestOne finishes, the web page will load again, and execute TestTwo, which means that the fixture set_up was executed again, which is not what I want (since I set set_up scope to module, so it should only really happen once).
I know there is a similar question asked here (py.test method to be executed only once per run), but the asker didn't have the constraint of needing TestBase to have a fixture applied to it as well.
I have thought of putting the fixture inside conftest.py, but I am not sure if it is possible given my constraint of the fixture needing to be inside a class, and executed only once.
Any help will be appreciated. Thank you!
In your code, your set_up fixture which is module scope is called before your TestBase resolves the driver_get fixture. Because of this, at set_up trying self.driver will raise an AttributeError: object has no attribute 'driver'.
One quick way to fix this in your example code is to refer to the driver_get fixture in your module set_up fixture like so:
class TestBase:
#pytest.fixture(scope="module", autouse=True)
def set_up(self, driver_get): # <-------------
self.driver.get("https://www.yahoo.com")
Another way to refer to fixtures is just by including the fixture name as an argument.
Personally I am not a huge fan of the approach you copied from that blog of setting a class attribute on the request node. You'd get an IDE warning about referring to self.driver. To me, it would be clearer to yield the driver out of driver_get and then within the test classes either set it to self in a setup style fixture or use it directly. Similar to something below.
#pytest.fixture(scope="session")
def driver_get():
from selenium import webdriver
web_driver = webdriver.Chrome()
yield web_driver
web_driver.close()
class TestClass:
#pytest.fixture(autouse=True)
def setup(self, driver_get):
self.driver = driver_get
def test_something(self):
self.driver.get("https://www.google.com")
But depending on what else needs to be setup, if you want to control it happening just once per session or module you would need to modify the approach a bit.
Programing is a new thing for me and probably I am missing something obvious.
I would like to create a separate file and class for setup my webdriver for Appium tests, but I got errors like:
in test_login main_page = MainPage(self.driver)
AttributeError: 'test_Login_iOS' object has no attribute 'driver'
Currently I have two files: one with test case and other with methods for test steps:
test_Login_iOS.py:
class test_Login_iOS(unittest.TestCase):
def setUp(self):
logging.info("WebDriver request initiated. Waiting for response, this may take a while.")
# choose desired capabilities from desired_capabilities.py
desired_capabilities = DesiredCapabilities.desired_capabilities_for_iOS_iPad
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", desired_capabilities)
self.driver.implicitly_wait(15) # seconds
def test_login(self):
logging.info("starting Test Case 1: login into active account")
welcome_page = WelcomePage(self.driver)
welcome_page.click_login_button()
login_page = LoginPage(self.driver)
and second file, page_ios.py:
class BasePage(unittest.TestCase):
"""
:type driver: appium.webdriver.Remote
"""
def __init__(self, driver):
super().__init__()
self.driver = driver
When I add new test case I have to add to it the same setUp method like in previous test case, so I would like to create a new class "Setup" that could be shared across multiple test cases.
Goal is to move setUp method to separate file and new class.
I have a fixture that returns a value like this:
import pytest
#pytest.yield_fixture(scope="module")
def oneTimeSetUp(browser):
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
yield driver
print("Running one time tearDown")
This fixture gets the browser value from another fixture which is reading the command line option.
Then I have a test class where I have more than one test methods and they all want to consume the same returned value driver to proceed the tests.
import pytest
#pytest.mark.usefixtures("oneTimeSetUp")
class TestClassDemo():
def test_methodA(self):
# I would like to use the driver value here
# How could I do this?
# Something like this
self.driver.get("https://www.google.com")
self.driver.find_element(By.ID, "some id")
print("Running method A")
def test_methodB(self):
print("Running method B")
Using self.driver fails with the error message
self = <test_class_demo.TestClassDemo object at 0x102fb6c18>
def test_methodA(self):
> self.driver.get("https://www.google.com")
E AttributeError: 'TestClassDemo' object has no attribute 'driver'
I am aware that I can pass the fixture as an argument to every method where I want to use that, but that is not the best way because I need this in every method and it should be possible to pass it to the class and then use it in all the test methods.
What is the best way that I can make the driver object available to the methods?
EDIT 1:
Created the fixture in conftest.py like this as suggested
#pytest.yield_fixture(scope="class") # <-- note class scope
def oneTimeSetUp(request, browser): # <-- note the additional `request` param
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(3)
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
## add `driver` attribute to the class under test -->
if request.cls is not None:
request.cls.driver = driver
## <--
yield driver
print("Running one time tearDown")
I have one more class, which object in need in the TestClassDemo and I need to pass the same driver instance to the class. Consider it as class ABC
class ABC():
def __init(self, driver):
self.driver = driver
def enterName(self):
# Do something with driver instance
Then in the TestClassDemo
#pytest.mark.usefixtures("oneTimeSetUp", "setUp")
class TestClassDemo(unittest.TestCase):
# I need to create an object of class ABC, so that I can use it here
# abc = ABC(self.driver)
#pytest.fixture(scope="class", autouse=True)
def setup(self):
self.abc = ABC(self.driver)
# I tried this, but it's not working
# This error message shows up
# AttributeError: 'TestClassDemo' object has no attribute 'driver'
def setup_module(self):
self.abc = ABC(self.driver)
# This also does not work
# Error message -> AttributeError: 'TestClassDemo' object has no attribute 'abc'
def test_methodA(self):
self.driver.get("https://google.com")
self.abc.enterName("test")
print("Running method A")
def test_methodB(self):
self.abc.enterName("test")
print("Running method B")
This abc object should be usable in other test_ methods also.
All these classes are in separate modules, I mean to say in separate .py files.
Also please explain in the answer what is the best way to use instead of yield driver instance.
EDIT 2:
For this example without yield, what would be the best way to run oneTimeTearDown also? I was running the tearDown steps after the yield
#pytest.fixture(scope="class")
def oneTimeSetUp(request, browser):
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(3)
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
if request.cls is not None:
request.cls.driver = driver
Also I tried using UnitTest class, but when I use def setUpClass(cls), I was not able to use the objects instantiated in the test_ methods. So I couldn't not figure out how to achieve that.
I also wanted to provide command line arguments like browser from the command line and when I tried unittest, I had to write the command line argument in every class. I wanted to provide them in one place only, like a test suite. So conftest helped me here.
I had a question on stackoverflow but didn't get a response. Could you please take a look at that also?
Python unittest passing arguments to parent test class
Thanks
Thanks
There's a technique outlined in the py.text unittest integration documentation that may be helpful to you ... using the built-in request fixture. Otherwise, I'm not aware of way to access the return value of a fixture without providing the named fixture as a method param.
#pytest.yield_fixture(scope="class") # <-- note class scope
def oneTimeSetUp(request, browser): # <-- note the additional `request` param
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
## add `driver` attribute to the class under test -->
if request.cls is not None:
request.cls.driver = driver
## <--
yield driver
print("Running one time tearDown")
Now you can access the driver as a class attribute in TestClassDemo, as you have in your example (i.e. self.driver should work).
The caveat is that your fixture must use scope='class', otherwise the request object will not possess a cls attribute.
I hope that helps!
UPDATE
I have one more class, which object in need in the TestClassDemo and I need to pass the same driver instance to the class. Consider it as class ABC
It's difficult to know without more context, but it seems to me that you can probably get away with instantiating an ABC object at the same time that you instantiate the driver ... in the oneTimeSetUp fixture. For example ...
#pytest.yield_fixture(scope="class")
def oneTimeSetUp(request, browser):
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(3)
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
if request.cls is not None:
request.cls.driver = driver
request.cls.abc = ABC(driver) # <-- here
yield driver
print("Running one time tearDown")
But if you only need the ABC instance for a test class or two, here's how you might use a fixture inside the class definition ...
#pytest.mark.usefixtures("oneTimeSetUp", "setUp")
class TestClassDemo(unittest.TestCase):
#pytest.fixture(autouse=True)
def build_abc(self, oneTimeSetUp): # <-- note the oneTimeSetup reference here
self.abc = ABC(self.driver)
def test_methodA(self):
self.driver.get("https://google.com")
self.abc.enterName("test")
print("Running method A")
def test_methodB(self):
self.abc.enterName("test")
print("Running method B")
I wouldn't be particularly happy with the second example. A third option would be to have another yield_fixture, or similar, that is completely separate from oneTimeSetUp and returns an ABC instance with the driver already wrapped.
Which way is best for you? Not sure. You'll need to decide based on what you're working with.
It's proper to note for posterity that pytest fixtures are just sugar and a bit of magic. You are not required to use them at all, if you find them difficult. pytest is happy to execute vanilla unittest TestCases.
Also please explain in the answer what is the best way to use instead of yield driver instance.
Here's what I had in mind ...
#pytest.fixture(scope="class")
def oneTimeSetUp(request, browser):
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(3)
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
if request.cls is not None:
request.cls.driver = driver
... notice that this doesn't return (or yield) the driver object, which means that it's no longer useful to provide this fixture as a named parameter to a function/method, which should be fine if all of your test cases are written as classes (suggested by your examples).
However, if you want to use the fixture as a named parameter, don't do this.
My code is like this:
class class1(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
def testListRolesTitle(self):
driver=self.driver
driver.get("www.google.com")
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
asert...
class class2(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
def testListRolesTitle(self):
driver=self.driver
driver.get("www.google.com")
assert...
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
def suite():
s1 = unittest.TestLoader().loadTestsFromTestCase(class1)
s2 = unittest.TestLoader().loadTestsFromTestCase(class2)
return unittest.TestSuite([s1,s2])
if __name__ == "__main__":
run(suite())
When I ran the suite both of the test classes started a new firefox instance in setup methord.
My question is if it's possible to make the two test classed use the same firefox instance?
I don't want to put them together in one class.
Any ideas?
You can have a setup function that applies to the whole module instead of just to the class as explained here.
In your case, that would be something like:
def setUpModule():
DRIVER = webdriver.Firefox()
def tearDownModule():
DRIVER.quit()
Note that DRIVER is a global variable in this case so that it's available to the objects of all classes.
Also, note that test case ordering might cause that your module setup functions are called multiple times as explained in the documentation:
The default ordering of tests created by the unittest test loaders is to group all tests from the same modules and classes together. This will lead to setUpClass / setUpModule (etc) being called exactly once per class and module. If you randomize the order, so that tests from different modules and classes are adjacent to each other, then these shared fixture functions may be called multiple times in a single test run.
It think this example should make clear when each setup method/function is executed:
import unittest
def setUpModule():
print 'Module setup...'
def tearDownModule():
print 'Module teardown...'
class Test(unittest.TestCase):
def setUp(self):
print 'Class setup...'
def tearDown(self):
print 'Class teardown...'
def test_one(self):
print 'One'
def test_two(self):
print 'Two'
The output from this is:
$ python -m unittest my_test.py
Module setup...
Class setup...
One
Class teardown...
.Class setup...
Two
Class teardown...
.Module teardown...
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK