I recently found the SeleniumIDE extension for Google Chrome, but there is something I don't understand...
from selenium import webdriver
from selenium.webdriver.common.by import By
class TestStealth():
def setup_method(self, method):
print("setup_method")
self.driver = webdriver.Chrome()
self.vars = {}
def teardown_method(self, method):
self.driver.quit()
def test_stealth(self):
print("Testing")
self.driver.get("https://stealthxio.myshopify.com/products/stealthxio-practice")
self.driver.set_window_size(968, 1039)
this is the code I get from selenium, when I try to run with:
run = TestStealth()
run.setup_method()
run.test_stealth()
I get an error in run.setup_method() as:
Missing 1 required positional argument: 'method'
Does anyone know what I am doing wrong?
This error message...
Missing 1 required positional argument: 'method'
...implies that the setup_method() is missing a required positional argument, i.e. 'method'
Analysis
You were pretty close. As per the defination of setup_method(self, method) it expects an argument as method.
def setup_method(self, method):
But when you invoked setup_method() as in:
run.setup_method()
You haven't passed any argument. Hence there was a argument mismatch and you see the error.
Solution
To execute your tests with in the Class using Selenium you can use the following solution:
Code Block:
class TestStealth():
def setup_method(self):
print("setup_method")
self.driver = webdriver.Chrome(executable_path=r'C:\WebDrivers\chromedriver.exe')
self.vars = {}
def teardown_method(self):
self.driver.quit()
def test_stealth(self):
print("Testing")
self.driver.get("https://stealthxio.myshopify.com/products/stealthxio-practice")
self.driver.set_window_size(968, 1039)
run = TestStealth()
run.setup_method()
run.test_stealth()
Console Output:
setup_method
DevTools listening on ws://127.0.0.1:51558/devtools/browser/88bf2c58-10da-4b03-9697-eec415197e66
Testing
Browser Snapshot:
Related
*args unpacks the locators in tuple form.but is my case i have given only two arguments,but it is taking three arguments.Need help to understand.
I am newbie to selenium with python and played around some code from github,
but getting error.
TypeError: find_element_by_xpath() takes 2 positional arguments but 3 were given
locator.py
from selenium.webdriver.common.by import By
class elements(object):
Customer = (By.XPATH, "//button[contains(text(),'Customer')]")
base.py
from selenium import webdriver
from selenium.webdriver.common.by import By
class Page(object):
def __init__(self,driver,url=None):
self.url = url
self.driver = driver
def find_element_with_click(self,*locator):
self.driver.find_element_by_xpath(*locator).click()
pages.py
from selenium import webdriver
from base import Page
from locator import *
class CustomerCreation(Page):
def __init__(self, driver):
self.locator = elements
super().__init__(driver)
def create_customer(self):
self.driver.find_element_with_click(*self.locator.Customer)
testPages.py
import unittest
from selenium import webdriver
from pages import *
from locators import *
from selenium.webdriver.common.by import By
class TestPages(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome('C:\ChromeDriver\chromedriver')
cls.driver.get("#server")
def test_tes_cust(self):
page = CustomerCreation(self.driver)
res_page = page.create_customer() #Getting issue at this stage
#classmethod
def tearDownClass(cls):
cls.driver.close()
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase(TestPages)
unittest.TextTestRunner(verbosity=2).run(suite)
The error log:
test_tes_cust (main.TestPages) ... ERROR
======================================================================
ERROR: test_tes_cust (main.TestPages)
----------------------------------------------------------------------
Traceback (most recent call last):
File "testPages.py", line 28, in test_tes_cust
res_page = page.create_customer()
File "C:\Users###\PycharmProjects\basics\pages.py", line 35, in create_customer
self.find_element_with_click(*self.locator.Customer)
File "C:\Users###\PycharmProjects\basics\base.py", line 21, in find_element_with_click
self.driver.find_element_by_xpath(*locator).click()
TypeError: find_element_by_xpath() takes 2 positional arguments but 3 were given
You're passing one extra argument. Your arguments are:
self
By.XPATH
"//button[contains(text(),'Customer')]"
That's what you need to pass to find_element method. While find_element_by_xpath should take two arguments only:
self
"//button[contains(text(),'Customer')]"
So try to update your code as
def find_element_with_click(self,*locator):
self.driver.find_element(*locator).click()
or you need to modify your Customer as:
Customer = "//button[contains(text(),'Customer')]"
and
def find_element_with_click(self, xpath):
self.driver.find_element_by_xpath(xpath).click()
Is there any proper way to using getting url in cmd as argument along testcases.py file?
I am running below commmand in cmd to run test cases of python file:
testcases.py "any url"
testcases.py have coding:
class JSAlertCheck(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome("E:\chromedriver.exe")
self.url = sys.argv[1]
def test_Case1(self):
driver = self.driver
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
unittest.main(sys.argv[1])
As per the discussion Python unittest passing arguments the Python Pundits seems to convey that:
Unit tests should be stand-alone which will have no dependencies outside of their setUp() and tearDown() methods. This is to make sure that each tests has minimal side-effects and reactions to the other test. Passing in a parameter defeats this property of unittest and thus makes them sort of invalid. Using a Test Configuration would have been the easiest way and more appropiate as a unittest should never rely on foreign data to perform the test.
If you still want to do so, here is one of the working solution:
Code Block:
from selenium import webdriver
import unittest
import sys
class MyTest(unittest.TestCase):
URL = "foo"
def setUp(self):
self.driver = webdriver.Chrome(executable_path=r'C:\Utility\BrowserDrivers\chromedriver.exe')
driver = self.driver
driver.get(self.URL)
def test_Case1(self):
driver = self.driver
print(driver.title)
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
if len(sys.argv) > 1:
MyTest.URL = sys.argv.pop()
unittest.main()
CLI Command:
python unittest_cmdline_urlASarguments.py http://www.python.org
Output:
C:\Users\AtechM_03\LearnAutmation\PythonProject\readthedocs>python unittest_cmdline_urlASarguments.py http://www.python.org
[4448:5632:0606/205445.017:ERROR:install_util.cc(589)] Unable to create registry key HKLM\SOFTWARE\Policies\Google\Chrome for reading result=2
DevTools listening on ws://127.0.0.1:5634/devtools/browser/40cc6c16-1e52-4f49-a54f-08fac3ff7abc
Welcome to Python.org
.
----------------------------------------------------------------------
Ran 1 test in 9.534s
OK
C:\Users\AtechM_03\LearnAutmation\PythonProject\readthedocs>
Commandline Snapshot:
I try to add pytest_addoption(parser) in my confest.py. Here are the official Pytest docs
But if I try to start test I see
TypeError: __init__() got an unexpected keyword argument 'browser'
Confest.py
import pytest
from fixture.application import Application
__author__ = 'Max'
fixture = None
#pytest.fixture
def app(request):
global fixture
browser = request.config.getoption("--browser")
if fixture is None:
fixture = Application(browser=browser)
else:
if not fixture.is_valid:
fixture = Application(browser=browser)
fixture.session.ensure_login(username="somename", password="somepassword")
return fixture
def pytest_addoption(parser):
# hooks for browsers
parser.addoption("--browser", action="store", default="chrome")
fixture/application.py
from selenium import webdriver
class Application:
def __init__(self,browser):
if browser == "chrome":
self.wd = webdriver.Chrome()
elif browser == "firefox":
self.wd = webdriver.Firefox()
else:
raise ValueError("Unrecognized browser %s" % browser)
Solution
You should use Application(browser) (at Confest.py).
Another similar problem: __init__() got an unexpected keyword argument 'user'
Explanation
When you make Application(browser=browser), you are trying to use keyword parameters.
Example with keyword parameter
from selenium import webdriver
class Application:
def __init__(self, *args, **kwargs):
if kwargs['browser'] == "chrome":
self.wd = webdriver.Chrome()
elif kwargs['browser'] == "firefox":
self.wd = webdriver.Firefox()
else:
raise ValueError("Unrecognized browser %s" % kwargs['browser'])
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.
I am running a a selenium functional test using nose from inside a django function using:
arg = sys.argv[:1]
arg.append('--verbosity=2')
arg.append('-v')
out = nose.run(module=ft1.testy1, argv=arg, exit=False)
I have created the functional test using the selenium IDE. Part of the test looks like:
class y1(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
self.base_url = "https://www.yahoo.com/"
self.verificationErrors = []
self.accept_next_alert = True
def test_y1(self):
driver = self.driver
driver.get(self.base_url)
driver.find_element_by_link_text("Weather").click()
driver.save_screenshot('out1.png')
return " this is a returned value"
I want to return a string value (" this is a returned value") to the calling function. How can I do this?
The output of your nose.run() does not correspond to your test method. The default behavior for unittest.TestCase is to throw an exception. If you would like to signal an external code some specific detail, you can always do it through global/class variables, files, etc.
For example, this is how to do it with a class variable (results)
ft1_runner.py:
import nose
import ft1_test
if __name__ == '__main__':
out = nose.run(module=ft1_test, exit=False)
print 'Y1.test_y1 test results returned:', ft1_test.Y1.results['test_y1']
ft1_test.py:
import unittest
class Y1(unittest.TestCase):
results = {}
def test_y1(self):
Y1.results['test_y1'] = "this is a returned value"
I think it would help if you can describe the problem you are trying to solve: this seems a little awkward and error prone (what if the test is called twice, or test skipped, etc.)