django run selenium test - django

I have a problem to run selenium tests with separate django command. Default "test" command looks into "tests" folder and runs unittests ok. Problem is, i want to make folder "seleniumtests" and place there test files to run them with command "test_selenium". And i want this command to do the same as default django "test" but in another dir.
The tests.py with selenium:
from django_liveserver.testcases import LiveServerTestCase
from selenium.webdriver.firefox.webdriver import WebDriver
class MySeleniumTests(LiveServerTestCase):
# fixtures = ['test-data.json']
#classmethod
def setUpClass(cls):
cls.selenium = WebDriver()
super(MySeleniumTests, cls).setUpClass()
#classmethod
def tearDownClass(cls):
super(MySeleniumTests, cls).tearDownClass()
cls.selenium.quit()
def test_admin(self):
self.selenium.get(self.live_server_url +'/admin/')
self.assertIn("Django", self.selenium.title)

Follow this tutorial on how to put your tests into folders: http://www.pioverpi.net/2010/03/10/organizing-django-tests-into-folders/
in general:
from [Project Name].[App Name].tests.[filename] import *
from [Project Name].[App Name].seleniumtests.[selenium] import *
#starts the test suite
__test__= {
'your_django_tests': [filename],
'selenium': [selenium],
}

Related

Not able to inspect Django migrations from test but working from Django command

I want to be able to inspect Django migrations within a test, so I can check basic sanity before running more expansive tests.
I was able to come up with the following in a Django command:
from django.core.management.base import BaseCommand
from django.db.migrations.loader import MigrationLoader
class Command(BaseCommand):
help = "Check migrations"
def handle(self, *args, **options):
self.migration_loader = MigrationLoader(None)
self.migration_loader.build_graph()
for root_node in self.migration_loader.graph.root_nodes():
app_name = root_node[0]
print(app_name, root_node[0])
But when I translate that into a test (using pytest):
from django.db.migrations.loader import MigrationLoader
def test_multiple_leaf_nodes_in_migration_graph():
migration_loader = MigrationLoader(None)
migration_loader.build_graph()
for root_node in migration_loader.graph.root_nodes():
app_name = root_node[0]
print(app_name, root_node[0])
Then the graph (root nodes) returns an empty list.
The project is structured as follows:
django_project/
settings.py
... # other Django core files
tests/
test_above.py
django_app/
models.py
... # other files from this app
management/commands/
command_above.py
pytest.ini
pytest.ini:
[pytest]
DJANGO_SETTINGS_MODULE = django_project.settings
python_files = tests.py test_*.py
The command:
pytest --no-migrations
PS: the whole point of this is to be able to detect migration errors without running them.
Is there anything I need to do so I can "see" migrations within the test?
pytest --no-migrations
Since you disabled migrations, the MigrationLoader won't load the migrations.
To load the migrations, you can override settings for MIGRATION_MODULES just for that test:
from django.test import override_settings
#override_settings(MIGRATION_MODULES={}) # Add this
def test_multiple_leaf_nodes_in_migration_graph():
...

Simple Flask example with pytest and application factory does not work

I am new to flask and I have set up a simple flask example and two tests using pytest(see here). When I let run only one test it works, but if I run both tests it does not work.
Anyone knows why? I think I am missing here some basics of how flask works.
code structure:
app/__init__.py
from flask import Flask
def create_app():
app = Flask(__name__)
with app.app_context():
from app import views
return app
app/views.py
from flask import current_app as app
#app.route('/')
def index():
return 'Index Page'
#app.route('/hello')
def hello():
return 'Hello World!'
tests/conftest.py
import pytest
from app import create_app
#pytest.fixture
def client():
app = create_app()
yield app.test_client()
tests/test_app.py
from app import create_app
def test_index(client):
response = client.get("/")
assert response.data == b"Index Page"
def test_hello(client):
response = client.get("/hello")
assert response.data == b"Hello World!"
The problem is with your registration of the routes in app/views.py when you register them with current_app as app. I'm not sure how you would apply the application factory pattern without using blueprints as the pattern description in the documentation implies they are mandatory for the pattern:
If you are already using packages and blueprints for your application [...]
So I adjusted your code to use a blueprint instead:
app/main/__init__.py:
from flask import Blueprint
bp = Blueprint('main', __name__)
from app.main import views
app/views.py -> app/main/views.py:
from app.main import bp
#bp.route('/')
def index():
return 'Index Page'
#bp.route('/hello')
def hello():
return 'Hello World!'
app/__init__.py:
from flask import Flask
def create_app():
app = Flask(__name__)
# register routes with app instead of current_app:
from app.main import bp as main_bp
app.register_blueprint(main_bp)
return app
Then your tests work as intended:
$ python -m pytest tests
============================== test session starts ==============================
platform darwin -- Python 3.6.5, pytest-6.1.0, py-1.9.0, pluggy-0.13.1
rootdir: /Users/oschlueter/github/simple-flask-example-with-pytest
collected 2 items
tests/test_app.py .. [100%]
=============================== 2 passed in 0.02s ===============================

django selenium liveserver ignores css

When I create a test and I request a view I have two options.
Running the django dev server and call self.selenium.get('http://localhost:8000' + reverse('my_view'))
Using the selenium live server and call self.selenium.get(self.live_server_url + reverse('my_view'))
The problem with the first option is that it's a hardcoded location.
The problem with the second option is that the live server does not request css and js files. so its just pure HTML
How it should look:
How it actually looks:
Am I missing anything why the css and js files are not requested?
Base Testfile setup:
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.urls import reverse
from django.utils.translation import gettext as _
from selenium.webdriver.firefox.webdriver import WebDriver
class SeleniumTests(StaticLiveServerTestCase):
#classmethod
def setUpClass(cls):
super(SeleniumTests, cls).setUpClass()
cls.selenium = WebDriver()
lang_cookie = cls.selenium.get_cookie('django_language')
browser_lang = cls.selenium.execute_script("return window.navigator.userLanguage || window.navigator.language")
translation.activate(lang_cookie if lang_cookie != None else browser_lang)
#classmethod
def tearDownClass(cls):
cls.selenium.quit()
super(SeleniumTests, cls).tearDownClass()
def test_login_form(self):
url = self.live_server_url + reverse('...')
self.selenium.get(url)
I use geckodriver-v0.26.0-win64.

pytest and Django transactional database

I use production database for tests (actually it's test database in docker). The question is: how to make tests run in transactions against this database. I need the same behavior like #pytest.mark.django_db(transaction=True) but with production database.
Current setup:
conftest.py
#pytest.fixture(scope='session')
def django_db_setup():
"""Avoid creating/setting up the test database"""
pass
#pytest.fixture
def db(request, django_db_setup, django_db_blocker):
django_db_blocker.unblock()
#pytest.fixture
def myfixture(db):
...
return SomeObject
test_example.py
def test_something(db, myfixture):
assert ...
Finally I've found the solution.
Add fixtures loading code to db fixture:
conftest.py
from django.core.management import call_command
#pytest.fixture
def db(request, django_db_setup, django_db_blocker):
django_db_blocker.unblock()
call_command('loaddata', 'fixture.json')
And use #pytest.mark.django_db(transaction=True) with tests:
test_example.py
#pytest.mark.django_db(transaction=True)
def test_something(db, myfixture):
assert ...
After each test pytest will flush your database and fill it with fixtures data.

Django LiveTestServerCase not using proper settings

In a Django project I'm using selenium to run some UI tests, using a LiveServerTestCase.
One of my test cases is failing, and when using the Firefox driver I can see a page throwing "Server Error (500)", which means DEBUG is set to False which is not the case when I run the local development server.
How is the test server being launched? Why is not using my settings which define DEBUG = True?
Other URLs (such as the homepage URL) return fine, so the server is working. But I just don't get why it's not showing debug information, and which settings it's using.
My test case for reference:
class LoginTest(LiveServerTestCase):
#classmethod
def setUpClass(cls):
try:
from selenium.webdriver import PhantomJS
cls.selenium = PhantomJS()
except:
from selenium.webdriver.firefox.webdriver import WebDriver
cls.selenium = WebDriver()
super(LoginTest, cls).setUpClass()
#classmethod
def tearDownClass(cls):
cls.selenium.quit()
super(LoginTest, cls).tearDownClass()
def test_fb_login(self):
self.selenium.get('%s%s' % (self.live_server_url, reverse('account_login')))
# TEST SERVER RETURNS 500 ON THIS URL WITH NO DEBUG INFO
According to Testing Django Application - Django Documentation:
Regardless of the value of the DEBUG setting in your configuration
file, all Django tests run with DEBUG=False. This is to ensure that
the observed output of your code matches what will be seen in a
production setting.
It should still be possible to override this using:
with self.settings(DEBUG=True):
...
Although I wouldn't recommend it, it can still be useful from time to time. (Thomas Orozco's comment)
You can also change your settings in TestCase setUp() method.
from django.conf import settings
class MyTest(LiveServerTestCase):
def setUp(self):
# Change settings here
settings.DEBUG = True
# ...
I ran into the same issue and it is possible to override settings with a decorator.
based on your example you would import override_settings and place the decorator above the class:
from django.conf import settings
from django.test import override_settings
#override_settings(DEBUG=True)
class LoginTest(LiveServerTestCase):
...
details in django docs