Cannot pass object instance from conftest.py to a test class - selenium

I am trying to create a session-scope fixture that creates and pass instance of the driver class for every single test class in my suite. I was expecting that the following code would work:
import pytest
from pages.home.home_page import HomeAdmin
from base.webdriver_factory import WebDriverFactory
#pytest.fixture(scope='session', autouse=True)
def startup(request):
print("SESSION SET UP")
wdf = WebDriverFactory("firefox")
driver = wdf.get_web_driver_instance() # returns driver instance
return driver
I was expecting to have acces to the driver instance from my test code:
from pages.home.home_page import HomeAdmin
import unittest
import pytest
#pytest.mark.usefixtures("startup")
class HomeAdminTest(unittest.TestCase):
#pytest.fixture(autouse=True)
def setup(self, startup):
print("TEST")
self.ha = HomeAdmin(self.driver)
def test_login(self):
print("test run")
Buy this results in an error:
#pytest.fixture(autouse=True)
def setup(self, startup):
print("TEST")
> self.ha = HomeAdmin(self.driver)
E AttributeError: 'HomeAdminTest' object has no attribute 'driver'
testcases\home\home_test.py:11: AttributeError
What I'm trying to achieve in general:
Open browser only once for all tests (all classes and modules) and then run various other classes to manipulate with the same driver instance. (I know that this is not best practice for testing, but this is special case and I am going rather automate some processes than making real tests).
Thank you in advance,
Wojciech

Finally I figured out how to solve problem by myself. It turned out that I was refering to the instance of webdriver in the test class in wrong way. Properly working class looks as following:
from pages.home.home_page import HomeAdmin
import unittest
import pytest
#pytest.mark.usefixtures("startup")
class HomeAdminTest(unittest.TestCase):
#pytest.fixture(autouse=True)
def setup(self,startup):
print("TEST")
self.ha = HomeAdmin(startup)
def test_login(self):
print("test run")
Now I have access to my driver and page class created with it.

Related

How can I assign web elements to variables while avoiding StaleElementRefference in Selenium Python?

Is there any way to assign elements to variables in Python Selenium while avoiding StaleElementReference Exception?
Say I have the following code structure,
class PageObject:
def __init__(self, driver):
self.driver = driver
#property
def checkbox(self):
return self.driver.find_element_by_css_selector('selector')
...
class TestCases:
...
def test_case(self):
page = PageObject(self.driver)
chk = page.checkbox
assert(chk.get_attribute("checked") == "false")
chk.click()
assert(chk.get_attribute("checked") == "true")
This will raise a stale element reference error.
Is there any way to structure my code so that I am able to define an element in the page object class,
and then assign that element to a variable to be used in the test class?
Import this into your page object file:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
Then your method
#property
def checkbox(self):
return WebDriverWait(self.driver, 30).until(EC.presence_of_element_located((By.CSS_SELECTOR, "selector")))
Most probably is your element is not present when you are trying to get it.

How to create a callable mock from a non-callable spec?

I have two classes: class One that does some stuff and class Wrapper that translates the API of One. To test the Wrapper I want to mock the class One. My problem is that One sets a number of attributes in it's __init__ method and I'd like my test to throw an error when these attributes are changed (e.g. renamed or deleted) in One but not in Wrapper.
I have tried to initialize an instance of One and to use it as a spec, but the resulting mock is non-callable:
from unittest import mock
import pytest
class One:
def __init__(self, a):
self.a = a
spec_obj = One(a='foo')
# working code, the test should pass
class Wrapper:
def __init__(self, wa):
self._one = One(a=wa)
self.wa = self._one.a
#mock.patch(__name__ + '.One', autospec=spec_obj)
def test_wrapper(MockOne):
MockOne.return_value.a = test_a = 'bar'
wrapper_obj = Wrapper(wa=test_a)
MockOne.assert_called_once_with(a=test_a)
assert wrapper_obj.wa == test_a
which throws the error:
TypeError: 'NonCallableMagicMock' object is not callable
since the spec spec_obj is non-callable.
If I set autospec=True, everything works but the test passes even when the One's attribute is renamed.
MockOne.return_value produces a Mock that you configure, but when the mock is actually called, you get a different Mock that hasn't been properly configured. You need to configure MockOne.return_value directly.
#mock.patch('One', autospec=True)
def test_wrapper(MockOne):
MockOne.return_value.a = test_a = 'bar'
wrapper_obj = Wrapper(wa=test_a)
MockOne.assert_called_once_with(a=test_a)
assert wrapper_obj.wa == test_a

Flask-SqlAlchemy RuntimeError: No application found. Either work inside a view function or push an application context

I try to implement Flask-SqlAlchemy in a Quart application it MVC structure. So I want every Model Have its file and functions.
This is my files:
app.py
__package__ = 'nini'
from .setups import create_app
from .db import db
if __name__ == '__main__':
app = create_app()
db.init_app(app)
app.run(host='127.0.0.1', debug=True)
db.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
tests.py Model file
from nini.db import db
import datetime
class Test(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.VARCHAR)
created = db.Column(db.DATETIME, default=datetime.datetime.now())
pass
def __init__(self, text):
self.text = text
def __repr__(self):
return '<Test %r>' % self.id, self.created
#classmethod
async def create_new(cls, **kw):
obj = cls(**kw)
db.session.add(obj)
db.session.commit()
pass
test.py
import logging
from quart import jsonify, app, current_app
from quart_openapi import PintBlueprint
from datetime import datetime
from nini.models import tests
from nini.db import db
results = PintBlueprint('test', __name__)
#results.route('/test/test')
async def get_tests():
logging.error("HELLO")
t = await tests.Test.create_new(text="TT")
logging.error("Done")
return jsonify(t), 200
When I run /test/test path it works fine until `db.session.add(obj)1 line.
Then I get this error:
RuntimeError: No application found. Either work inside a view function
or push an application context. See
http://flask-sqlalchemy.pocoo.org/contexts/.
INFO:quart.serving:127.0.0.1:50537 GET /test/test 1.1 500 - 21836
I tried adding this code at app.py:
db.init_app(app) ### This line was already in my code
with app.app_context():
db.create_all()
Also like this:
db.init_app(app) ### This line was already in my code
app.app_context().push():
db.create_all()
I also tried running create_new at tasts.py with context.
Tried implementing the create_all and test model inside db.py
Basically after db.init_app(app) whenever I use db I get the error.
Would love for some help.

How to run two different page object methods in a single test case in webdriver using POM

I have created a POM model in my webdriver framework where i have two pages one is login page and the other is users page, now i have written a test case for login page and it is working fine, the problem comes when i am trying to run a second test case where i need to login first in order to reach to the users page where i need to click on the view page.
below is the piece of code which i am writing to run the two different page object model methods into a single test case , currently it is running the login method only not the users page method.![enter image description here][1]
Above i have attached the framework screenshot, and below is the userstestcase code which i have written :
It is not allowing me to paste the framework screenshot please help me out
package testCases;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import pageFactory.Userspage;
import pageFactory.loginPage;
public class UsersTestCase {
WebDriver driver;
loginPage lpg;
Userspage upg;
#BeforeTest
public void setup(){
driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("someurl");
}
#Test(priority=0)
public void test_login(){
lpg = new loginPage(driver);
String loginPageTitle = lpg.getLoginTitle();
Assert.assertTrue(loginPageTitle.contains("Login"));
lpg.loginToGuru99("username", "password");
driver.findElement(By.xpath("//a[#href='somelink']")).click();
}
public void test_users(){
upg = new Userspage(driver);
String usersPageTitle = upg.getLoginTitle();
Assert.assertTrue(usersPageTitle.contains("Users"));
}
}
EDIT:
a quick fix would be:
Move the below
lpg = new loginPage(driver);
upg = new Userspage(driver);
to the beforeTest step, right under:
driver.get("http://citysurfstaging.sourcefuse.com/admin/login");
and in your user test, before anything else, call the line below:
lpg.loginToGuru99("saurabh.singh#sourcefuse.com", "sourcefuse123");
driver.findElement(By.xpath("//a[#href='http://citysurfstaging.sourcefuse.com/admin/users']")).click();
That should put you in the state you need to perform the user test...
Quick advice, if you really want to do POM, then you shouldnt be mapping elements in your tests, that should all be done within your page class...
ORIGINAL:
If you have multiple pages, and need them to interact with each other to perform an end to end test, why dont you create another layer of abstraction to the POM and have a Flow class?
Example:
In the constructor of the flow class you instantiate the pages you need to perform a flow (loginpage, userpage), which will give you visibility of the page objects of each page class, then create as many flows (methods) between these pages. The next step would be instantiate the flow in your test (the same way you instantiated the page) and call your flow methods which interacts with as many pages you wish... if this is not clear enough I can give more detailed examples
The above question has been resolved what i have done to resolve this is i haven't declared the global webdriver command and then using this command i wasn't transferring the control to the users page but i have recreated my framework and everything is working fine now

How do Scrapy from_settings and from_crawler class methods work?

I need to add the following class method to my existing pipeline
http://doc.scrapy.org/en/latest/faq.html#i-m-getting-an-error-cannot-import-name-crawler
i am not sure how to have 2 of these class methods in my class
from twisted.enterprise import adbapi
import MySQLdb.cursors
class MySQLStorePipeline(object):
"""A pipeline to store the item in a MySQL database.
This implementation uses Twisted's asynchronous database API.
"""
def __init__(self, dbpool):
self.dbpool = dbpool
#classmethod
def from_settings(cls, settings):
dbargs = dict(
host= settings['DB_HOST'],
db= settings['DB_NAME'],
user= settings['DB_USER'],
passwd= settings['DB_PASSWD'],
charset='utf8',
use_unicode=True,
)
dbpool = adbapi.ConnectionPool('MySQLdb', **dbargs)
return cls(dbpool)
def process_item(self, item, spider):
pass
From my understanding of class methods, several class methods in a python class should just be fine. It just depends on which one the caller requires. However, I have only seen from_crawler until now in scrapy pipelines. From there you can get access to the settings via crawler.settings
Are you sure that from_settings is required? I did not check all occurences, but in middleware.py priority seems to apply: If a crawler object is available and a from_crawler method exists, this is taken. Otherwise, if there is a from_settings method, that is taken. Otherwise, the raw constructor is taken.
if crawler and hasattr(mwcls, 'from_crawler'):
mw = mwcls.from_crawler(crawler)
elif hasattr(mwcls, 'from_settings'):
mw = mwcls.from_settings(settings)
else:
mw = mwcls()
I admit, I do not know if this is also the place where pipelines get created (I guess not, but there is no pipelines.py), but the implementation seems very reasonable.
So, I'd just either:
reimplement the whole method as from_crawler and only use that one
add method from_crawler and use both
The new method could look like follows (to duplicate as little code as possible):
#classmethod
def from_crawler(cls, crawler):
obj = cls.from_settings(crawler.settings)
obj.do_something_on_me_with_crawler(crawler)
return obj
Of course this depends a bit on what you need.