how to avoid attribute error from classes? - python

I made a class as the basic setup of the selenium driver. and I added methods to it. I want to access method of the class from a context manager I tried to create an object of the class in the context manager and access the method but I failed. how do I do it?
I even do not have any idea whats wrong in this code. please help me.
class SeleniumDriver:
'''basic setup for chromedriver(selenium)'''
def __init__(self,
driversource='C:\\Users\Ewis\Downloads\chromedriver.exe',
url = ('https://realpython.com/')
):
self.driversource = driversource
self.url = url # this tuple
def __enter__(self):
self.driver = webdriver.Chrome(executable_path=self.driversource)
for urls in self.url:
self.driver.get(urls)
return self.driver, Keys
def __exit__(self, exc_type, exc_val, exc_trace):
self.driver.quit()
# and this code(another project)
from seleniumdriver import SeleniumDriver
with SeleniumDriver() as packed:
seldriver1 = SeleniumDriver()
driver = packed[0]
urls = ('https://realpython.com/','https://stackoverflow.com/')
resp = seldriver1.geturl(urls)
for url in resp:
title, url = resp.titleurl()
print(title, '\n', url,'\n')
Error :
Traceback (most recent call last):
File "C:\python\progs\web\selenium\navigation_commands\navigation_commands.py", line 9, in
for url in resp:
File "c:\python\progs\my_modules\seleniumdriver\seleniumdriver.py", line 22, in geturl
yield (self.driver).get(url)
AttributeError: 'SeleniumDriver' object has no attribute 'driver'

I'm not sure if it is enough to resolve all problems but:
You have to use packed[0] which is created using with and it executes __enter__ which creates self.driver
resp = packed[0].geturl(urls)
But you use seldriver1 which is created using seldriver1 = SeleniumDriver() which doesn't execute __enter__ so it doesn't create self.driver (seldriver1.driver) and you get error:
'SeleniumDriver' object has no attribute 'driver'
from seleniumdriver import SeleniumDriver
with SeleniumDriver() as packed:
urls = ('https://realpython.com/','https://stackoverflow.com/')
resp = packed[0].geturl(urls)
for url in resp:
title, url = resp.titleurl()
print(title, '\n', url,'\n')
Your code seems weird for me and it may need much more changes to work correctly.
EDIT: this code works for me.
Because I use Firefox on Linux and I don't have to set driversource so I added None to run without driversource
I removed get(url) from __enter__ because it is useless. You can't get two pages in __enter__ and use them later in for loop because Selenium doesn't keep information about first page when you open second page.
from selenium import webdriver
class SeleniumDriver:
'''basic setup for chromedriver(selenium)'''
def __init__(self, driversource='C:\\Users\Ewis\Downloads\chromedriver.exe'):
self.driversource = driversource
def __enter__(self):
if self.driversource:
#self.driver = webdriver.Chrome(executable_path=self.driversource)
self.driver = webdriver.Firefox(executable_path=self.driversource)
else:
#self.driver = webdriver.Chrome()
self.driver = webdriver.Firefox()
return self.driver
def __exit__(self, exc_type, exc_val, exc_trace):
self.driver.quit()
with SeleniumDriver(None) as driver:
urls = ('https://realpython.com/', 'https://stackoverflow.com/')
for url in urls:
driver.get(url)
title = driver.title
print(title, '\n', url,'\n')

Related

how to resolve NoneType' object has no attribute 'get'

While running the below code getting error " NoneType' object has no attribute 'get'
"
TestProductRecogic.py:
import unittest
from PageObject.ProductRcognic import ProductPage
import pytest
class TestProductRecognic(unittest.TestCase):
#pytest.fixture(autouse=True)
def classSetup(self, setup):
self.driver = setup
def test_Product(self):
self.driver.get("https://www.recognic.ai/")
self.ep = ProductPage(self.driver)
self.ep.clickOnProduct()
def tearDown(self):
self.driver.close()
Ensure that your setup is returning the driver instance.
You would need to have a setup fixture which looks somewhat like this:
#pytest.fixture()
def setup():
driver = webdriver.Chrome(path) #example
return driver
class TestProductRecognic(unittest.TestCase):
#pytest.fixture(autouse=True)
def classSetup(self, setup):
self.driver = setup
def test_Product(self):
self.driver.get("https://www.recognic.ai/")
self.ep = ProductPage(self.driver)
self.ep.clickOnProduct()
def tearDown(self):
self.driver.close()

AttributeError: 'Context' object has no attribute 'app'

Hello I did not found answer for similar problem so I add new topic.
I have problem with bdd + appium using a page object model. When I run my script I have issue:
Traceback (most recent call last):
File "/home/mimy/.local/lib/python3.8/site-packages/behave/model.py", line 1329, in run
match.run(runner.context)
File "/home/mimy/.local/lib/python3.8/site-packages/behave/matchers.py", line 98, in run
self.func(context, *args, **kwargs)
File "features/steps/allow_to_app_steps.py", line 6, in tap_allow_when_using_app
context.app.launch_page.tap_allow_button()
File "/home/mimy/.local/lib/python3.8/site-packages/behave/runner.py", line 321, in __getattr__
raise AttributeError(msg)
AttributeError: 'Context' object has no attribute 'app'
My environment.py file looks like this:
from appium import webdriver
from app.application import Application
def before_scenario(context, scenario):
desired_capabilities = {
"platformName": "Android",
"platformVersion": "10",
"deviceName": "Pixel 2 XL",
"appPackage": "com.xxx.xxx",
"appActivity": ".ui.MainActivity",
"automationName": "UiAutomator2"
}
context.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_capabilities=desired_capabilities)
context.driver.implicitly_wait(5)
context.app = Application(context.driver)
def after_scenario(context, scenario):
context.driver.quit()
My steps file looks like this:
from behave import given, when, then, step
#given('I click on the "Allow only while using the app" button')
def tap_allow_when_using_app(context):
context.app.launch_page.tap_allow_button()
#when('I click on the "Allow" button')
def tap_allow(context):
context.app.launch_page.tap_allow()
My pages file for my page object model looks like:
###LunchPage###
from selenium.webdriver.common.by import By
from pages.base_page import Page
class LaunchPage(Page):
dialog_title = (By.XPATH, "//android.widget.TextView[contains(#text,'Allow QSpot to access this device')]")
allow_only_while_using_the_app = (By.XPATH, "//android.widget.Button[#text='Allow only while using the app']")
allow = (By.XPATH, "//android.widget.Button[#text='Allow']")
def tap_allow_button(self):
self.click(*self.allow_only_while_using_the_app)
def tap_allow(self):
self.click(*self.allow)
###BasePage###
class Page:
def __init__(self, driver):
self.driver = driver
def find_element(self, *locator):
return self.driver.find_element(*locator)
def click(self, *locator):
e = self.find_element(*locator)
e.click()
And class Application
from pages.launch_page import LaunchPage
class Application:
def __init__(self, driver):
self.launch_page = LaunchPage(driver)
As I now this issue may be related with "driver was not starting" but I am not able to fix it.
Many thanks for help!
In my case, I missed up names in architecture names.
So after changing from "enviroment" to "environment" (missing N).
it became alive.

Subclassed object results in NoneType

I'm trying to subclass the Chrome WebDriver to include some initialization and cleanup code, but then Python complains that the created object is set to None:
import glob
import selenium
import subprocess
from selenium.webdriver.common.by import By
class WebDriver(selenium.webdriver.Chrome):
def __init__(self, url, *args, **kwargs):
super().__init__(*args, **kwargs)
self.url = url
def __enter__(self):
self.get(self.url)
self.implicitly_wait(15)
def __exit__(self, type, value, traceback):
self.quit()
for path in glob.glob('/tmp/.org.chromium.Chromium.*'):
subprocess.run(['rm', '-rf', path], check=True)
with WebDriver('https://google.com') as driver:
driver.find_element(By.ID, 'lst-ib').send_keys('Search')
Running the code with Python 3:
$ python3 test.py
Traceback (most recent call last):
File "test.py", line 43, in <module>
driver.find_element(By.ID, 'lst-ib').send_keys('Search')
AttributeError: 'NoneType' object has no attribute 'find_element'
Your __enter__() magic method should return self for the driver variable to be pointed to the instance of the WebDriver class:
def __enter__(self):
self.get(self.url)
self.implicitly_wait(15)
return self
To get more information about why and how this works, please see:
Understanding Python's "with" statement
Explaining Python's '__enter__' and '__exit__'

How to call method on every py.test assertion failure?

Background:
I'm using py.test together with pytest-selenium, now I would like to take a screenshot of page when assertion fails.
Currently I have defined small helper method in my base page object class:
class PageBase(object):
def __init__(self,driver):
self.driver = driver
self.fake = Factory.create()
def screenshot(self,name):
self.driver.save_screenshot(datetime.now().strftime('%Y-%m-%d %H:%M:%S') + 'scr_'+name+'.png')
#contextmanager
def wait_for_page_load(self, timeout=45):
old_page = self.driver.find_element_by_tag_name('html')
yield
WebDriverWait(self.driver, timeout).until(
EC.staleness_of(old_page)
)
The problem is that I would like to make it automated mechanism instead of "manual" usage:
(test class example):
class TestLogin:
#allure.feature('Ability to login into admin panel')
def test_admin_login(self, prepare, page):
print URLMap.admin('test')
driver = prepare
driver.get(URLMap.admin(page))
login_page = LoginPage(driver)
assert login_page.is_page_correct(),'Login page not loaded correctly'
login_page.fill_login_data('testadmin','testadmin')
login_page.click_login_button()
assert login_page.is_user_logged_in(),'User cannot log in with provided credentials'
login_page.screenshot(page+'_logged_in')
How to run certain method for every assertion failure?
I personally haven't used but this might be your solution:
https://pytest.org/latest/example/simple.html#writing-well-integrated-assertion-helpers
Also this could help:
https://pytest.org/latest/assert.html#advanced-assertion-introspection
You have to use hooks.
https://docs.pytest.org/en/latest/example/simple.html#post-process-test-reports-failures
#pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result()
setattr(item, "rep_" + rep.when, rep)
return rep
#pytest.fixture(autouse=True, scope='session')
def driver(platform, request):
""" some driver setup code """
yield driver
if request.node.rep_call.failed:
try:
driver.get_screenshot_as_png()
except:
pass
driver.quit()
And if you want to attach a screenshot to allure report, simply do:
#pytest.fixture(autouse=True, scope='session')
def driver(platform, request):
""" some driver setup code """
yield driver
if request.node.rep_call.failed:
# Make the screen-shot if test failed:
try:
allure.attach(
driver.get_screenshot_as_png(),
name=request.function.__name__,
attachment_type=allure.attachment_type.PNG)
except:
""" do something """
driver.quit()
I think that screenShotInSelenium page should give you enough information regarding how you create a screenshot when an assert condition is met.
What you are missing is the use of #AfterMethod

Cannot call method in other class

Here is the page object file: login.py
from pages.base import BasePage
from config import secrets
from selenium.webdriver.common.keys import Keys
class LoginPage(BasePage):
def __init__(self):
self.webdriver = BasePage.webdriver
port = raw_input("Enter port number: ")
self.url = "http://localhost:" + port
#property
def retrieve_username_field(self):
self.webdriver.find_element_by_name("username")
#property
def retrieve_password_field(self):
self.webdriver.find_element_by_name("password")
def login(self, username=None, password=None):
username = username or secrets.username
password = password or secrets.password
self.retrieve_username_field.send_keys(username)
self.retrieve_password_field.send_keys(password)
self.retrieve_password_field.send_keys(Keys.RETURN)
Here is the base page file: base.py
from selenium import webdriver
class BasePage(object):
webdriver = webdriver.Firefox()
def go(self):
self.webdriver.get(self.url)
Here is the test file: test_login.py
import unittest
from pages.login import LoginPage
login_page = LoginPage()
def setUpModule():
login_page.go()
def tearUpModule():
login_page.logout()
class TestLogin(unittest.TestCase):
def test_login_succeeds_with_valid_credentials(self):
login_page.login()
xpath = "//th[text() = 'Spool Name']"
self.assertIsNotNone(login_page.webdriver.find_element_by_xpath(xpath))
if __name__ == "__main__":
unittest.main()
The problem is that I get this error: http://puu.sh/9JgRd/e61f5acec3.png and I'm not sure why I cannot call login method. I have reference to LoginPage object but failure happens exactly here.
Your problem is not that you can't call login(), but that self.retrieve_username_field returns None and thus does not have a send_keys method.
That's exactly what the error you get is telling you.

Categories