Why pytest.mark.parametrize does not work with classes in pytest? - python

Following code does not collect any test cases (i expect 4 to be found). Why?
import pytest
import uuid
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
class TestClass:
def __init__(self):
self.browser = webdriver.Remote(
desired_capabilities=webdriver.DesiredCapabilities.FIREFOX,
command_executor='http://my-selenium:4444/wd/hub'
)
#pytest.mark.parametrize('data', [1,2,3,4])
def test_buybuttons(self, data):
self.browser.get('http://example.com/' + data)
assert '<noindex>' not in self.browser.page_source
def __del__(self):
self.browser.quit()
If i remove __init__ and __del__ methods, it will collect tests correctly. But how i can setup and tear test down? :/

pytest won't collect test classes with an __init__ method, a more detailed explanation of why is that can be found here: py.test skips test class if constructor is defined.
You should use fixtures to define setup and tear down operations, as they are more powerful and flexible.
If you have existing tests that already have setup/tear-down methods and want to convert them to pytest, this is one straightforward approach:
class TestClass:
#pytest.yield_fixture(autouse=True)
def init_browser(self):
self.browser = webdriver.Remote(
desired_capabilities=webdriver.DesiredCapabilities.FIREFOX,
command_executor='http://my-selenium:4444/wd/hub'
)
yield # everything after 'yield' is executed on tear-down
self.browser.quit()
#pytest.mark.parametrize('data', [1,2,3,4])
def test_buybuttons(self, data):
self.browser.get('http://example.com/' + data)
assert '<noindex>' not in self.browser.page_source
More details can be found here: autouse fixtures and accessing other fixtures

Related

Problem with unittest -- receiving str object has no attribute 'get'

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.

How do i make the pytest driver instance available in my testcase

I am trying to build an selenium based automation framework, using, Python, Pytest.
My intention is to create a driver instance at the class level by initializing it in conftest.py and making it available in all testcases, so that user doesn't need to create the driver instance in each testcase.
Driver instance in conftest.py:
#pytest.fixture(scope="class")
def get_driver(request):
from selenium import webdriver
driver = webdriver.Chrome()
request.cls.driver = driver
yield
driver.quit()
BaseTestCase class looks like this:
#pytest.mark.usefixtures("get_driver")
class BaseTestCase(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(BaseTestCase, self).__init__(*args, **kwargs)
#classmethod
def setUpClass(cls):
if hasattr(super(BaseTestCase, cls), "setUpClass"):
super(BaseTestCase, cls).setUpClass()
My testcase is as follows:
from ..pages.google import Google
class Test_Google_Page(BaseTestCase):
#classmethod
def setUpClass(self):
self.page = Google(self.driver, "https://www.google.com/")
My page Google extends to BasePage that looks like this:
class BasePage(object):
def __init__(self, driver, url=None, root_element = 'body'):
super(BasePage, self).__init__()
self.driver = driver
self._root_element = root_element
self.driver.set_script_timeout(script_timeout)
When i execute my testcase, I get the following error:
#classmethod
def setUpClass(self):
> driver = self.driver
E AttributeError: type object 'Test_Google_Page' has no attribute 'driver'
How can I make the driver instance available in my testcases by simply calling self.driver?
The class scoped fixtures are executed after the setUpClass classmethods, so when Test_Google_Page.setUpClass is executed, get_driver did not run yet. Check out the execution ordering:
import unittest
import pytest
#pytest.fixture(scope='class')
def fixture_class_scoped(request):
print(f'{request.cls.__name__}::fixture_class_scoped()')
#pytest.mark.usefixtures('fixture_class_scoped')
class TestCls(unittest.TestCase):
#classmethod
def setUpClass(cls):
print(f'{cls.__name__}::setUpClass()')
def setUp(self):
print(f'{self.__class__.__name__}::setUp()')
#pytest.fixture()
def fixture_instance_scoped(self):
print(f'{self.__class__.__name__}::fixture_instance_scoped()')
#pytest.mark.usefixtures('fixture_instance_scoped')
def test_bar(self):
print(f'{self.__class__.__name__}::test_bar()')
assert True
When running the test with e.g. pytest -sv, the output yields:
TestCls::setUpClass()
TestCls::fixture_class_scoped()
TestCls::fixture_instance_scoped()
TestCls::setUp()
TestCls::test_bar()
So the solution is to move the code from setUpClass to e.g. setUp:
class Test_Google_Page(BaseTestCase):
def setUp(self):
self.page = Google(self.driver, "https://www.google.com/")
I am afraid I may not use setUp because in my most of my class file I have multiple test cases and I just want to do one time setup in setUpClass instead of launching in each time before calling any test method.
I would then move the code from setUpClass to another class-scoped fixture:
import pytest
#pytest.mark.usefixtures('get_driver')
#pytest.fixture(scope='class')
def init_google_page(request):
request.cls.page = Google(request.cls.driver,
"https://www.google.com/")
#pytest.mark.usefixtures('init_google_page')
class Test_Google_Page(BaseTestCase):
...
The former setUpClass is now the init_google_page fixture which will be called after get_driver (because of pytest.mark.usefixtures('get_driver')).

Python Selenium run multiple test classes in one selenium webdriver using with pytest

I am new in python and I started to create an automation test suite about a GUI(multiple test cases in different file). I want to execute all my test cases in one selenium webdriver, so I created a singleton webdriver class and I want to use this class all my test cases. Here is my singleton webdriver class:
from selenium import webdriver
class Webdriver(object):
"""
Singleton class based on overriding the __new__ method
"""
def __new__(cls):
"""
Override __new__ method to control the obj. creation
:return: Singleton obj.
"""
if not hasattr(cls, 'instance'):
cls.instance = super(Webdriver, cls).__new__(cls)
return cls.instance
#staticmethod
def get_driver():
"""
:return: Selenium driver
"""
dir = "C:\\Python36\\Lib\\site-packages\\selenium\\webdriver\\ie"
ie_driver_path = dir + "\\IEDriverServer.exe"
return webdriver.Ie(ie_driver_path)
and my setUp example:
from Core.Webdriver import Webdriver
class LoginExternal(unittest.TestCase):
def setUp(self):
# Create Instances
self.web = Webdriver.get_driver()
self.matcher = TemplateMatcher()
self.gif = GifCapture("C:\\wifi\\Videos\\" + self._testMethodName + ".gif")
self.gif.start()
time.sleep(3)
def test_LoginExternal(self):
# Open External Login Page
self.web.maximize_window()
Here is my problem, when I execute my test suite, my code create a new selenium instance but I want only one selenium instance to be used in all test cases.
I use pytest as a test runner with following cmd command:
pytest --pyargs --html=Report/External_04052018.html ExternalTestSuite/
I think the problem is pytest use a new proccess in every test case execution. Is there any way to prevent or use like this way?
Pytest greatest feature and advantage over traditional XUnit family test runners is that it has fixtures. I invite you to use it. In your scenario, I would get rid of extending unittest.TestCase and setUp method in favor of pytest fixture as follows:
import pytest
from Core.Webdriver import Webdriver
class TestLoginExternal:
#pytest.fixture(scope='class')
def driver(self):
print('Setup runs once before all tests in class')
driver = Webdriver.get_driver()
yield driver
driver.quit()
print('Teardown runs once after all tests in class')
def test_LoginExternal(self, driver):
# Open External Login Page
print('test_LoginExternal')
def test_LoginInternal(self, driver):
# Open External Login Page
print('test_LoginInternal')

Running all selenium tests inside a test case

I have multiple tests inside one test case but I am noticing that it is only running the first test
import unittest
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.support.ui import WebDriverWait
class Test(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.base_url = "http://www.example.com/"
def test_Google(self):
driver = self.driver
driver.implicitly_wait(10)
driver.get(self.base_url)
def fill_contact(self):
driver.find_element_by_xpath('//a[contains(.,"Contact")]').click()
driver.implicitly_wait(10)
driver.find_element_by_xpath('//input[#type="submit"][#value="Send"]').click()
# def tearDown(self):
# self.driver.quit()
if __name__ == "__main__":
unittest.main()
Whenever I run this it only runs
def test_Google(self)
and nothing after that. am I doing something wrong?
Methods must begin with 'test' to be automatically run.
Per the docs:
A testcase is created by subclassing unittest.TestCase. The three
individual tests are defined with methods whose names start with the
letters test. This naming convention informs the test runner about
which methods represent tests. (my emphasis)
The TestLoader is responsible for loading test and returning them wrapped in a TestSuite. It uses this method to identify tests:
class TestLoader(object):
testMethodPrefix = 'test'
def getTestCaseNames(self, testCaseClass):
"""Return a sorted sequence of method names found within testCaseClass
"""
def isTestMethod(attrname, testCaseClass=testCaseClass,
prefix=self.testMethodPrefix):
return attrname.startswith(prefix) and \
hasattr(getattr(testCaseClass, attrname), '__call__')
testFnNames = filter(isTestMethod, dir(testCaseClass))
...
Thus, the attrname.startswith(prefix) checks if the method name begins with 'test'.
As an alternative to what #unubtu noted:
you can use a nose test runner and mark a method with #istest decorator:
from nose.tools import istest
class Test(unittest.TestCase):
...
#istest
def fill_contact(self):
driver.find_element_by_xpath('//a[contains(.,"Contact")]').click()
driver.implicitly_wait(10)
driver.find_element_by_xpath('//input[#type="submit"][#value="Send"]').click()
Besides, here is a pretty good overview of the unittest test discovery:
How does Python's unittest module detect test cases?

Python nose setup/teardown class fixture method not executed for test generators

In a hobby project I intend to use nose for testing, I want to put all tests for specific classes into classes since these tests share setup and other functionality. But I can't seem to get nose to execute the setup methods inside the classes.
Here is an example class that is tested:
class mwe():
def __init__(self):
self.example = ""
def setExample(self, ex):
self.example = ex
The tests work, when I don't use classes:
from nose.tools import ok_
import mwe
exampleList = []
def setUp():
print("setup")
exampleList.append("1")
exampleList.append("2")
exampleList.append("3")
def test_Example():
print("test")
for ex in exampleList:
t = mwe.mwe()
t.setExample(ex)
yield check, t, ex
def check(e, ex):
ok_(e.example == ex)
The output is as expected:
setup
test
...
----------------------------------------------------------------------
Ran 3 tests in 0.004s
OK
When a test class is used, the setup method is not executed and therefore no tests are executed.
from nose.tools import ok_
import mwe
class TestexampleClass(object):
def __init__(self):
print("__init__")
self.exampleList = []
def setup(self):
print("setup class")
self.exampleList.append("1")
self.exampleList.append("2")
self.exampleList.append("3")
def test_ExampleClass(self):
print("test class")
for ex in self.exampleList:
t = mwe.mwe()
t.setExample(ex)
yield self.check, t, ex
def check(self, we, ex):
print("check class")
ok_(we.example == ex)
I'm fairly new to python and a newbie with nose, my question is, why is setup not executed? Where is the error in my code?
__init__
test class
----------------------------------------------------------------------
Ran 0 tests in 0.002s
OK
I will be glad for any feedback.
When I use the code from this Question on SO the setup method is executed, as I would expect it.
SOLUTION: After a lot of desperation I found the following:
Nose executes the the class level setup method before the execution of the yielded function, not when the test_* methods are called, as I expected and as is the case for other test_* methods. This obviously goes against the nose documentation:
Setup and teardown functions may be used with test generators. However, please note that setup and teardown attributes attached to the generator function will execute only once. To execute fixtures for each yielded test, attach the setup and teardown attributes to the function that is yielded, or yield a callable object instance with setup and teardown attributes.
Looking at the bug reports, I found the bug report on github.
A possible workaround is to use class level fixtures:
#classmethod
def setup_class(cls):
#do stuff
pass
Your test class needs to extend TestCase and the setup method needs to be called setUp
from unittest import TestCase
class TestUtils(TestCase):
def setUp(self):
self.x = 1
def test_something(self):
self.assertEqual(1, self.x)
which outputs
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK

Categories

Resources