I am writing a program in python that will generate more robust data and reports from Selenium tests. I want to be able to export a test case from Selenium IDE from Firefox, and without modifying that specific exported python file, inject it into my script and allow my script to make modifications such as changing the webdriver (part of the implementation is for a Nagios server, which will use PhantomJS as the driver) and pulling request information and screenshots for reporting (Btw, I know I can change the driver in the IDE template, but it's just an example).
Lets say I have a file exported from the IDE path/to/mytests/search.py:
import...
class Search(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.base_url = 'http://www.example.com'
def test_search(self):
driver = self.driver
driver.get(self.base_url)
elem = self.driver.find_element_by_name('q')
elem.send_keys('nagios')
elem.send_keys(Keys.RETURN)
assert 'No results found.' not in self.driver.page_source
driver.get(self.base_url + '/status')
assert 'OK' in self.driver.page_source
def tearDown(self):
self.driver.quit()
I want my script to be able to do this:
python -m my_wrapper mytests.search --url 'http://www.google.com'
Which will somehow import search.py, create a new testcase from it, and allow me to override the setUp/tearDown methods (changing the driver to PhantomJS, modifying the base URL from the sys.argv), and allow for catching errors and writing information related to where a failed test might have stopped (since there are two different URL calls, a screenshot of that page will be exported for wherever the driver is currently at).
I'm thinking something like my_wrapper.__main__:
# get 'mytests.search' from argparse
dirname, module = os.path.split('mytests.search'.replace('.', '/'))
suite = unittest.TestLoader().discover(dirname, pattern='%s.py' % module)
# Modify testcases here <-- THIS IS THE HARD PART
#
# Is there a way to inject the tests into another testCase class?
#
# class BaseClass(unittest.TestCase):
# def setUp(self):
# self.driver = webdriver.PhantomJS()
# ...
#
# def runTest(self): pass
#
# def tearDown(self): ...
#
# then...
new_suite = unittest.TestSuite()
for sub_suite in master_suite: #<-- iterating over the above suite here
for tests in sub_suite._tests: #<-- why do you have to do this twice?
for test_name in tests._tests:
# This doesn't work but show's my thought process
mytest = BaseClass(methodName=test_name)
setattr(mytest, test_func, getattr(tests, test_name))
new_suite.addTest(mytest)
try:
result = unittest.TextTestRunner(verbosity=2).run(new_suite)
except (SOME_SELENIUM_ERROR):
# get the screenshot, save to file <-- don't know how to do this either
Anyway, any help into this would be appreciated. I would really like to keep this code as simple as possible using as much of what's available as I can.
Note: Other than selenium and PhantomJS, I'm also wary of using other dependencies like non-standard libraries, etc, for a number of policy reasons.
...
Hi my friend i have been searchin something like this for few days. I found this question in stackoverflow
Python unittest passing arguments
so you code should be like this:
import sys
import unittest
class Search(unittest.TestCase):
URL = ""
def setUp(self):
self.driver = webdriver.Firefox()
self.base_url = 'http://www.example.com'
def test_search(self):
driver = self.driver
driver.get(self.base_url)
elem = self.driver.find_element_by_name('q')
elem.send_keys('nagios')
elem.send_keys(Keys.RETURN)
assert 'No results found.' not in self.driver.page_source
driver.get(self.base_url + '/status')
assert 'OK' in self.driver.page_source
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
Search.URL=sys.argv.pop()
unittest.main()
I hope this is what you was looking for.
to execute you should do:
python mySearchTest.py "http://example.com"
Related
Hi I'm trying to learn Python/Selenium using a Page Object Model.
I have been following this tutorial and as far as I can tell I haven't missed anything. However I can't run my tests because of the init constructor. In the video, he has the same init constructor and the tests run for him.
Inside my project folder I have a subfolder pageObjects and testCases.
In pageObjects I have LoginPage.py where i set up my locators and actions i want to perform on the locators
class LoginPage:
textbox_username_id = "Email"
textbox_password_id = "Password"
button_login_xpath = "//input[#class='button-1 login-button']"
link_logout_linktext = "Logout"
def __init__(self,driver):
self.driver = driver
def setUserName(self,username):
self.driver.find_element_by_id(self.textbox_username_id).clear()
self.driver.find_element_by_id(self.textbox_username_id).send_keys(username)
def setPassword(self,password):
self.driver.find_element_by_id(self.textbox_password_id).clear()
self.driver.find_element_by_id(self.textbox_password_id).send_keys(password)
def clickLogin(self):
self.driver.find_element_by_xpath(self.button_login_xpath).click()
def clickLogout(self):
self.driver.find_element_by_link_text(self.link_logout_linktext).click()
In testCases I have test_login.py and conftest.py
I'll show conftest.py first
from selenium import webdriver
import pytest
#pytest.fixture()
def setup():
driver = webdriver.Chrome()
return driver
and test_login.py
import pytest
from selenium import webdriver
from pageObjects.LoginPage import LoginPage
from testCases import conftest
class Test_001_Login:
baseURL = "https://admin=demo.nopcommerce.com/"
username = "admin#yourstore.com"
password = "admin"
def __init__(self):
self.lp = LoginPage(self.driver)
self.driver = conftest.setup
def test_homePageTitle(self, setup):
self.driver = setup
self.driver.get(self.baseURL)
act_title = self.driver.title
self.driver.close()
if act_title == "Your store. Login":
assert True
else:
assert False
def test_login(self, setup):
self.driver = setup
self.driver.get(self.baseURL)
self.lp.setUserName(self.username)
self.lp.setPassword(self.password)
self.lp.clickLogin()
act_title = self.driver.title
if act_title == "Dashboard / nopCommerce administration":
assert True
else:
assert False
There are 2 issues.
conftest.py is supposed to be a special type of class/fixture. I should be able to call it to create an instance of webdriver.Chrome(). However it does not work. In test_login.py when i call 'setup' I get an error 'unresolved reference 'setup''. So I've had to manually import it as a work around. Isn't the whole point of the conftest.py file so that my test_* files have access to the webdriver in conftest.py wihtout having to import it?
When i try to run the test_login.py file, I get a warning and 0 items are collected - testCases\test_login.py:6
C:\Users\Mirza\PycharmProjects\nopcommerceApp\testCases\test_login.py:6: PytestCollectionWarning: cannot collect test class 'Test_001_Login' because it has a init constructor (from: testCases/test_login.py)
class Test_001_Login:
I have absolutely no idea what to do here. Why can't my class Test_001_Login have an init constructor? I need it to create an instance of the LoginPage class and to create an instance of the webdriver from conftest.py.
Any help would be greatly appreciated.
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)
import unittest
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
class CorrecaoEfetivaNota(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome('/Users/r13/dev/chromedriver')
def teste_login_avaliador(self):
driver = self.driver
driver.get("")
cpf = driver.find_element_by_xpath('//input[#placeholder="CPF"]')
cpf.send_keys("")
password = driver.find_element_by_xpath('//input[#placeholder="SENHA"]')
password.send_keys("")
login = driver.find_element_by_tag_name('button')
login.click()
driver.implicitly_wait(3)
def teste_buscar_mais_um(self):
driver = self.driver
buscar = driver.find_element_by_xpath("//section[1]/div/div/section[2]/div/div/div[1]/div/div[2]/button")
buscar.click()
def tearDown(self):
self.driver.close()
I'm trying to write this tests in Python, the first function is ok, but the second one inside the class is not being executed in the tests. How can I organize this?
While working with Python and unittest module with Selenium you have to consider a few facts as follows :
Indentation for class and test_method are different.
Instead of driver.close() always invoke driver.quit() within tearDown(){} method to close & destroy the WebDriver and Web Client instances gracefully.
If you are using unittest module you have to call the __main__.
Here is your own code with the required minor modifications which will execute the first method teste_login_avaliador() as well as the second method teste_buscar_mais_um() within the Class CorrecaoEfetivaNota():
import unittest
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
class CorrecaoEfetivaNota(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome(executable_path=r'C:\WebDrivers\chromedriver.exe')
def teste_login_avaliador(self):
driver = self.driver
driver.get("http://d3dyod5mwyu6xk.cloudfront.net/")
cpf = driver.find_element_by_xpath('//input[#placeholder="CPF"]')
cpf.send_keys("27922797885")
password = driver.find_element_by_xpath('//input[#placeholder="SENHA"]')
password.send_keys("enccejaregular")
login = driver.find_element_by_tag_name('button')
login.click()
driver.implicitly_wait(3)
def teste_buscar_mais_um(self):
driver = self.driver
buscar = driver.find_element_by_xpath("//section[1]/div/div/section[2]/div/div/div[1]/div/div[2]/button")
buscar.click()
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
unittest.main()
Note: Though both the test_methods are being called still you will face the following exception:
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//section[1]/div/div/section[2]/div/div/div[1]/div/div[2]/button"}
At the line:
buscar = driver.find_element_by_xpath("//section[1]/div/div/section[2]/div/div/div[1]/div/div[2]/button")
This exception can be solved easily following the actual Test Steps of your usecase and if required you can raise a new question/ticket.
You write that the first function is ok, which I assume must be the setUp() function you are referring to (provided that in your code you indented correctly).
As Andersson comments, your unittest methods needs to begin with "test_" not "teste_". Providing "test_" is your way of telling unittest that this method should be tested.
In your unittest you probably also want to test something such as self.assertEqual(1,1) otherwise your tests will pass no matter what.
Next time please provide us with a more thorough description of what is wrong. How did you make the call to unittest? What error is python giving you? What result did you expect? Etc. It makes solving your problem much faster.
I encourage you to make a simple test first and ensure that it runs:
import unittest
class TestingUnittest(unittest.TestCase):
def setUp(self):
print("SetUp called")
def tearDown(self):
print("tearDown called")
def test_Method(self):
print("Testing 1==1")
self.assertEqual(1,1)
Call this from your terminal:
>>>python -m unittest "name-of-test.py"
I'm incredibly new to separating modules. I have this long Python script that I want to separate into different files by class and run them collectively in the same browser instance/window. The reason for this is all the tests are reliant on being logged into the same session. I'd like to do a universal setUp, then login, and then pull the different tests in one after another.
Folder structure is:
ContentCreator
- main.py
- _init_.py
- Features
- login.py
- pytest.py
- _init_.py
Here is my code:
login.py
import unittest
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
import time
import json
driver = webdriver.Chrome()
class logIn(unittest.TestCase):
#classmethod
def test_login(self):
"""Login"""
driver.get("sign_in_url")
# load username and pw through a json file
with open('path/to/file.json', 'r') as f:
config = json.load(f)
# login
driver.find_element_by_id("email").click()
driver.find_element_by_id("email").clear()
driver.find_element_by_id("email").send_keys(config['user']['name'])
driver.find_element_by_id("password").click()
driver.find_element_by_id("password").clear()
driver.find_element_by_id("password").send_keys(config['user']['password'])
driver.find_element_by_id("submit").click()
time.sleep(3)
print("You are Logged In!")
pytest.py
import time
import unittest
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from displays import DISPLAY_TYPES, DISPLAY_NAMES
driver = webdriver.Chrome()
#driver.get("url")
class createContent(unittest.TestCase):
#classmethod
def test_add_required(self):
"""Test adding all the required fields across all sites:"""
for i in range(1):
"""This is the number of each type of article that will be created."""
for i in range(1):
"""This is how many different article types that will be created."""
print("create new content")
time.sleep(1)
driver.find_element_by_link_text("Content").click()
time.sleep(1)
driver.find_element_by_link_text("Create New").click()
print("select a display type:")
display = DISPLAY_TYPES
display_type = driver.find_element_by_id(display[i])
display_type.click()
names = (DISPLAY_NAMES[i])
print(names), (" created and saved successfully!")
#classmethod
def tearDownClass(cls):
# close the browser window
driver.quit()
def is_element_present(self, how, what):
"""
Helper method to confirm the presence of an element on page
:params how: By locator type
:params what: locator value
"""
try:
driver.find_element(by=how, value=what)
except NoSuchElementException:
return False
return True
main.py
import unittest
from HtmlTestRunner import HTMLTestRunner
from features.login import logIn
from features.pytest import createContent
login_script = unittest.TestLoader().loadTestsFromTestCase(logIn)
add_pytest = unittest.TestLoader().loadTestsFromTestCase(createContent)
# create a test suite combining all tests
test_suite = unittest.TestSuite([login, add_pytest])
# create output
runner = HTMLTestRunner(output='Test Results')
# run the suite
runner.run(test_suite)
When running the above code it opens two browser sessions, and only the login script get executed. The test fails do to not finding the elements outlined in the next script.
EDIT:
Alfonso Jimenez or anyone else, here's what I have so far...
Folder structure:
- Files
- singleton.py
- singleton2.py
New Singleton code...
singleton.py:
from robot.api import logger
from robot.utils import asserts
from selenium import webdriver
class Singleton(object):
instance = None
def __new__(cls, base_url, browser='chrome'):
if cls.instance is None:
i = object.__new__(cls)
cls.instance = i
cls.base_url = base_url
cls.browser = browser
if browser == "chrome":
# Create a new instance of the Chrome driver
cls.driver = webdriver.Chrome()
else:
# Sorry, we can't help you right now.
asserts.fail("Support for Chrome only!")
else:
i = cls.instance
return i
singleton2.py:
import time
import json
from datetime import datetime
from singleton import Singleton
driver = Singleton('base_url')
def teardown_module(module):
driver.quit()
class logIn(object):
def test_login(self):
"""Login"""
driver.get("url.com")
# load username and pw through a json file
with open('file.json', 'r') as f:
config = json.load(f)
# login
driver.find_element_by_id("email").click()
driver.find_element_by_id("email").clear()
driver.find_element_by_id("email").send_keys(config['user']['name'])
driver.find_element_by_id("password").click()
driver.find_element_by_id("password").clear()
driver.find_element_by_id("password").send_keys(config['user']['password'])
driver.find_element_by_id("submit").click()
time.sleep(3)
print("You are Logged In!")
# take screenshot
driver.save_screenshot('path/screenshot_{}.png'.format(datetime.now()))
The result is that an instance of Chrome kicks off, but nothing happens. The base_url (or any other URL defined in my test) doesn't come up. The blank window is all I get. Any insights on what I'm doing wrong?
You're instantiating two times the selenium driver.
If you want to keep the same session opened you should pass the same object to both scripts, or import it, what it could be valid, however it would be a more dirty solution.
The best thing to do is create a singleton class to initiate the driver. Once you have done this, every time you create an object from this class you will get the a unique object of webdriver.
You can get an example from this answer.
You can also check more about singleton instances, they're a very common and very useful. You can check here.
I dont understand what you mean with robot, perhaps the testing framework?
You can write the singleton class wherever you want to. You will have to import the class from that place and then instantiate the object. Ex:
lib/singleton_web_driver.py
from robot.api import logger
from robot.utils import asserts
from selenium import webdriver
class Singleton(object):
instance = None
def __new__(cls, base_url, browser='firefox'):
if cls.instance is None:
i = object.__new__(cls)
cls.instance = i
cls.base_url = base_url
cls.browser = browser
if browser == "firefox":
# Create a new instance of the Firefox driver
cls.driver = webdriver.Firefox()
elif browser == "remote":
# Create a new instance of the Chrome driver
cls.driver = webdriver.Remote("http://localhost:4444/wd/hub", webdriver.DesiredCapabilities.HTMLUNITWITHJS)
else:
# Sorry, we can't help you right now.
asserts.fail("Support for Firefox or Remote only!")
else:
i = cls.instance
return i
and then in every script youre going to need the webdriver:
test_script_file.py
from lib.singleton_web_driver import Singleton
driver = Singleton('base_url')
This just a dummy code, I dindnt test it. The important point is to create the class with the _new_ method where you can check if the class has already been called. The import is just like any other class import, you write the class in a folder and then import it in the scripts youre going to use.
I had a similar problem. My solution was just to initiate the driver in the main file and then to use this driver within the imported files and functions. Like in this example to change createContent(unittest.TestCase) to createContent(unittest.TestCase, driver)
I started selenium-server-standalone-2.29.0 server. I have python 2.7. I also downloaded selenium package. When I run this script I expect Firefox should be launched. But nothing happens. Am I missing something?
import unittest,selenium
class NewTest(unittest.TestCase):
def setUp(self):
self.selenium = selenium("localhost", 4444, "*firefox",
"http://www.google.com/")
self.selenium.start()
def test_new(self):
self.selenium.open("/")
selenium.type("q","selenium rc")
def tearDown(self):
self.selenium.stop()
Perhaps something like this instead?
import unittest
from selenium import webdriver
class NewTest(unittest.TestCase):
def setUp(self):
self.browser = webdriver.Firefox()
def test_new(self):
self.browser.get("http://www.google.com")
elem = self.browser.switch_to_active_element()
elem.send_keys('some search query')
def tearDown(self):
self.browser.quit()
Note, for testing stuff like this, I would err to use BDD testing (perhaps with lettuce) instead...