Using Selenium inside a class - python

I'm not very experience with writing classes and somewhat confused by their behavior.
test.py
class Profile:
def __init__(self):
self.browser = webdriver.Chrome()
def browser_object(self):
return webdriver.Chrome()
from the terminal:
from test import Profile
# I thought this would initialize webdriver.Chrome() and a browser window would pop up
x = Profile.browser_object()
# trying to access the webdriver so I can do something like
x.get(url) #from the terminal
No browser window is popping up. When I run x I see that it is a <function Profile.browser_object ...>
What am I doing wrong?

Try changing your other script to actually make an instance of the class you defined in test.py.
from test import Profile
profile_instance = Profile() # make an instance
browser_I_made = profile_instance.browser_object() # you probably wouldn't do this, but it's possible
I don't think you understand that a class is like a factory and you get "instances" of that object off the assembly line like in profile_instance = Profile()

Related

PyQT : How to pass a webdriver object from QThread to UI thread?

I'm making a program that automatically enters to a website and login(basically using selenium and chrome webdriver). User can type own information(id&pw) and site address in a dialog(used PyQt4 module). After finishing it, pressing ok button will execute it.
After logging in, I want to do some other actions with that webdriver objects.
So my question is, how can I pass the webdriver object to the main thread(which is UI thread here) so that I can do some other actions(such as logging out, etc), or how to manage that webdriver object generated in other thread in main thread?
I'm using Python3.7.4 and PyQt4 version.
I googled similar questions and found that it might be related with signal&slots.
so I tried to imitate this example (https://nikolak.com/pyqt-threading-tutorial/) which uses custom signal.
In this example, it passes a QString instance to the main thread(UI thread).
So I tried to pass my webdriver object by imitating it, but It's not going well...
The basic structure of code is following:
class MyDialog(QDialog):
def __init__(self):
QDialog.__init__(self)
# and some codes with widget layouts
def btnOkClicked(self):
a = [self.editSite1.text(), self.editId.text(), self.editPw.text()]
self.gothread = goWebsiteThread(a)
# 'goWebsiteThread' is a thread that generates webdriver object and executes login function
self.connect(self.gothread, SIGNAL("add_driver(PyQt_PyObject)"), self.add_driver)
# this line is what I tried to pass the driver object to this main thread
self.gothread.start()
class goWebsiteThread(QThread, QObject):
# I tried to pass this class's object by making this class inherit QObject class... sorry for unfounded try..
def __init__(self, sites):
QThread.__init__(self)
self.sites = sites
def goWebsite(self):
self.driver = webdriver.Chrome('./chromedriver.exe', options=options)
self.driver.get(some site address that user typed)
# and codes to log in
self.emit(SIGNAL('add_driver(PyQt.PyObject)'), self.driver)
# I tried to pass the driver object by emitting signal...
def run(self):
self.goWebsite()
But this doesn't work (MyDialog object doesn't recognize the driver object).
How can I properly pass the webdriver object to MyDialog object and use it?
Webdriver should run on a new thread and it would be impossible to control webdriver from a UI thread.
But you can still store the webdriver as a member variable of the UI instance.
If you want to send some commands to the webdriver, you would need to start a new thread and handle automation works in the newly created thread.

python wrapper; correct naming for method calling

I want to wrap the selenium webdriver in my own class, that each time I will call a method from my class it will handle calling and error handling to the webdriver class. what is the correct way to do it ?
class myClass():
browser = ... selenium web driver ...
def find_element_by_xpath(self, value):
try
browser.find_element_by_xpath(value)
except:
....
can myClass have the same method name ?
There are multiple valid ways to handle the calling and error handling of the webdriver class and yours should be fine.
Yes, myClass can have the same method names but you need to make sure you're calling the right thing. E.g
myClassInstance = myClass()
myClassInstance.find_element_by_xpath('thing')
will call browser.find_element_by_xpath just fine

How to define test methods using Python Unittest

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"

How do I import selenium webdriver tests from one Python file to another?

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)

Import Selenium IDE script as unittest.TestCase and Modify Dynamically

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"

Categories

Resources