Initially I have def setup() with options to call specific web browsers from another file that have all the information on how to setup the web browser. Remarking out the browser not used.
def setUp(self):
# Choose the Web Browser to test with
operabrowser(self)
# chromebrowser(self)
...
def test_one()
...
def test_two()
...
I am trying to move away from editing the test file each time, I am looking to setup a console input that will call the browser and I have added to def setup().
browser_choice = input ( """Choose your browser
Opera, [Firefox], Chrome or Safari
> """ ).lower ()
if browser_choice == 'opera':
operabrowser()
else chromebrowser()
This works with a single unit test but if there is more than one test in the file it asks each time for a browser choice.
How can I get this option to be asked only once for all tests that will be ran in the test file? I have tried a few other ways of approaching this all unsuccessful.
Tests should be defined as class methods, in your code they appears to be standalone functions not directly related with the class which holds the setUp() method. You just need to modify your code like this:
import unittest
class YouTests(unittest.TestCase):
def setUp(self):
q = 'Choose your browser Opera, [Firefox], Chrome or Safari >'
browser_choice = input (q).lower ()
if browser_choice == 'opera':
operabrowser()
else:
chromebrowser()
def test_one(self):
pass
def test_two(self):
pass
...
If you have multiple classes inheriting from unittest.TestCase and all should use the same browser the browser choice will be best inserted in your global scope. For example:
browser_object = None
...
def operabrowser():
global browser_object
# Init opera object here
def chromebrowser():
global browser_object
# Init chrome object here
...
# Your tests using the browser_object
...
if __name__ == '__main__':
q = 'Choose your browser Opera, [Firefox], Chrome or Safari >'
browser_choice = input (q).lower ()
if browser_choice == 'opera':
operabrowser()
elif browser_choice == 'firefox':
...
else:
chromebrowser()
unittest.main()
Related
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 am having a bit of trouble with nesting classes in python.
Mind you my code below is a simplified example showing what I want to do, but basically I want to use nested classes to make my code more structured and make sure I don't run into name clashes for certain functions.
See here my example code:
class Chrome:
def __init__(self, url='http://localhost:4723/wd/hub'):
# Capabilities
capabilities = Capabilities.chrome()
# Start session
self.driver = webdriver.Remote(url, capabilities)
def get_url(self, url):
# Go to URL
self.driver.get(url)
class actions:
#staticmethod
def browse_url(url):
# Go to url
Chrome.get_url(url)
if __name__ == '__main__':
browser = Chrome()
browser.actions.browse_url('https://www.google.com')
The goal as you can see in if __name__ == '__main__' is to be able to start a browser instance, and then call functions in a structured way.
However I have no clue on how to correctly achieve the browser.actions.browse_url('https://www.google.com') concept.
How is this done correctly ?
You should call get_url from an instance of Chrome and not the class itself, since it's an instance method and not a static one:
...
#staticmethod
def browse_url(url):
Chrome().get_url(url)
...
if __name__ == '__main__':
Chrome.actions.browse_url('https://www.google.com')
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 have a parent test class named as basetestcase()
This is inherited by all the test classes
class BaseTestCase(unittest.TestCase):
driver = None
browser = read from command line
operatingSystem = read from command line
url = read from command line
#classmethod
def setUpClass(cls):
"""
SetUp to initialize webdriver session, pages and other needed objects
Returns:
None
"""
# Get webdriver instance
# Browser should be read from the arguments
if browser == "iexplorer":
cls.driver = webdriver.Ie()
elif browser == "firefox":
cls.driver = webdriver.Firefox()
elif browser == "chrome":
cls.driver = webdriver.Chrome()
else:
cls.driver = webdriver.PhantomJS()
# Similarly I want to get operating system and url also from command line
driver.get(url)
print("Tests are running on: " + operatingSystem)
Then I have two separate test classes:
class TestClass1(BaseTestCase):
#classmethod
def setUpClass(cls):
super(TestClass1, cls).setUpClass()
# Create object of another class to use in the test class
# cls.abc = ABC()
def test_methodA(self):
# self.abc.methodFromABC() # This does not work
# Not sure if I can use self.driver as it was defined as cls.driver in the setUpClass()
self.driver.find_element(By.ID, "test_id").click()
if __name__ == '__main__':
unittest.main(verbosity=2)
This is the 2nd class, both the classes are in separate .py files
class TestClass2(GUIBaseTestCase):
#classmethod
def setUpClass(self):
super(TestClass2, self).setUpClass()
def test_methodA(self):
self.driver.find_element(By.ID, "test_id").click()
if __name__ == '__main__':
unittest.main(verbosity=2)
Then I have a test suite script, a separate .py file which clubs them together to run in a suite
import unittest
from tests.TestClass1 import TestClass1
from tests.TestClass2 import TestClass2
# Get all tests from TestClass1 and TestClass2
tc1 = unittest.TestLoader().loadTestsFromTestCase(TestClass1)
tc2 = unittest.TestLoader().loadTestsFromTestCase(TestClass2)
# Create a test suite combining TestClass1 and TestClass2
smokeTest = unittest.TestSuite([tc1, tc2])
unittest.TextTestRunner(verbosity=2).run(smokeTest)
I want to run the test suite and want to provide browser, operating system and url to the basetestcase from the command line and these arguments are directly used by basetestcase.py.
Actual test classes inherit the basetestcase.
Could you please help me with how to get these values from the command line in the best way and provide to the basetestcase?
I also struggled to run the same test cases on multiple browsers. After a lot of iterations, trial and error and input from friends I implemented the following solutions to my projects:
Here TestCase is the class that has all the tests and the browser driver is None. SafariTestCase inherits the TestCase and overrides setUpClass and sets the browser driver to be safari driver and same with the ChromeTestCase and you can add more class for other browsers. Command Line input can be taken in the TestSuite file and conditionally load tests based on the arguments:
class TestCase(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.browser = None
def test_1(self):
self.assert(self.browser.find_element_by_id('test1')
def test_2(self):
self.assert(self.browser.find_element_by_id('test2')
def test_3(self):
self.assert(self.browser.find_element_by_id('test3')
#classmethod
def tearDownClass(cls):
cls.browser.quit()
class SafariTestCase(TestCase):
#classmethod:
def setUpClass(cls):
cls.browser = webdriver.Safari(executable_path='/usr/bin/safaridriver')
class ChromeTestCase(TestCase):
#classmethod:
def setUpClass(cls):
cls.browser = webdriver.Chrome(executable_path='/usr/local/bin/chromedriver')
In the runner file TestSuite.py:
import TestCase as tc
if len(sys.argv) == 1:
print("Missing arguments, tell a couple of browsers to test against.")
sys.exit(1)
if sys.argv[1] == 'safari':
test = unittest.TestLoader().loadTestsFromTestCase(tc.SafariTestCase)
if sys.argv[1] == 'chrome':
test = unittest.TestLoader().loadTestsFromTestCase(lt.ChromeTestCase)
unittest.TextTestRunner().run(test)
I'm new to python with android. I need to write python script.
basically what I want to do is when you click first view, I need to load second view. second view has button, when it press that I need to load third view.
class SimpleAndroidTests(unittest.TestCase):
def setUp(self):
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '4.2'
desired_caps['deviceName'] = 'Android Emulator'
desired_caps['app'] = PATH(
'mypath/aaa.apk'
)
desired_caps['appPackage'] = 'com.xxxx'
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
def test_find_elementsFirstview(self):
time.sleep(13)
textfields = self.driver.find_elements_by_class_name("android.widget.EditText")
textfields[0].send_keys("first text")
textfields[1].send_keys("second text")
el = self.driver.find_element_by_name("press Login")
el.click()
def test_secondView(self):
time.sleep(10)
textfields = self.driver.find_elements_by_class_name("android.widget.EditText")
textfields[2].send_keys("secondviewinput2")
def tearDown(self):
# end the session
self.driver.quit()
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(SimpleAndroidTests)
unittest.TextTestRunner(verbosity=2).run(suite)
ISSUE is it is not entering to the second view. It is re-loding first view again.
please help...
In your code you have a method - setUp(self) - which nose tools interprets as a "test setup" method, meaning it will be run before each test. Since your setUp() method instantiates the webdriver it is reloading your app before each test. Consider moving your driver instantiation to a setUpClass(), which nose tools will run before all tests in the file.
Example test file:
class YourTest(unittest.TestCase):
#classmethod
def setUpClass(cls):
[...runs before all tests...]
def setUp(self):
[...runs before each test...]
def test_test1(self):
[...your tests...]
def tearDown(self):
[...runs after each test...]
#classmethod
def tearDownClass(cls):
[...runs after all tests...]
When an element on an android app is clicked via appium python script, then appium has no control over the expected behavior. It is the app that handles the click. So for example, if there is a Login button and if you do this:
el = self.driver.find_element_by_name("press Login")
el.click()
This click on the element is handled by the app and app will take over the flow from here. It is app that launches the next screen. For example, it could be a form.
Basically, appium python client has control over which UI element to choose, but what that particular UI element will do is up to the app.