How to access django database with pytest - django

I am wanting to use pytest with django to test api queries. Is there any way to do this in pytest without resorting to creating a test database? Pytest keeps throwing an error that database access is not allowed. I end up having to do this at the top of my pytest files:
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE","MyProject.settings")
import django
django.setup()
I tried using the pytest-django library, but it doesn't have a mark for accessing the db that I can use.

It always helps to rtfd:
https://pytest-django.readthedocs.io/en/latest/database.html
pytest-django takes a conservative approach to enabling database access. By default your tests will fail if they try to access the database. Only if you explicitly request database access will this be allowed. This encourages you to keep database-needing tests to a minimum which makes it very clear what code uses the database.

Related

django unittest to use "real" database

I am currently writing test cases for views, Which eventually uses database also.
By default a test database is being created and removed after test are run.
As the database itself is development database, I don't want my test to create a separate db but use exciting only.
Also I will like to bring to your notice, that in my environment, database are created and provided and django can't run migration or create database.
How can I create unittest which uses real database ?
I think the primary reason for this question is because your django database user is not provided with the create/drop database permission.
Django needs to create and drop a test database for the purpose of unit testing. It cannot use an existing database for this purpose. Why we are not allowed to use an existing database in the unit test is because, the data can be modified by anyone who has the same database permission and django may not have control over the updates they make, This might end up in an unsuccessful unit test.
This is clearly explained in another question's answer
If your DB Admin can provide your Django user the required access for the Test module to work as expected, You can make use of the Fixtures. Fixtures are like data files, can be created from your development environment and then can be used in the Unit test Setup to import the data from Fixtures to the test database created by Django.
The ultimate purpose of any Unit test framework will be to test the functionality of the Back end code logic with a data which is not likely to change. As mentioned in the above links, The Functional testing and Regression Testing is there to cover the real database.
For more details on Fixtures visit Using Fixtures with Django Test Cases

Services and flask_sqlalchemy inside a flask application

I'm building a large, complex Flask application. It has lots of route functions (which need to do database calls) and lots of services (which also need to do database calls).
I'm using flask_sqlalchemy to do the database calls in the route functions as normal with model syntax (User.query).
I would also like to make database calls in the services. However, I can't do this without setting an app context:
with app.app_context():
User.query
which requires importing the app, which leads to circular imports (because the app imports the routes, which import the services, which have to import the app).
QUESTION 1: Do you know a way to get round this circular import problem?
Another approach could be to use flask_sqlalchemy in the routes and sqlalchemy in the services. This means nothing needs to be shared except for the database url and.... the models.
QUESTION 2: Do you know a way to use the same model files with both flask_sqlalchemy and normal sqlalchemy?
I've tried this: https://dev.to/nestedsoftware/flask-and-sqlalchemy-without-the-flask-sqlalchemy-extension-3cf8
...however it breaks my relationships.
I am following the flask app pattern shown here: https://github.com/sloria/cookiecutter-flask/blob/master/%7B%7Bcookiecutter.app_name%7D%7D/README.rst (application factory)
Thanks,
Louise

Django Test with default database and without running any migrations

Is there any way I can use my default local database for testing in Django 1.9. I also don't want to run any migrations, and I want to test it locally.
The reason I want to do it this way is that in my migrations, I have a data migration referring to some entry from a model and when tests run and create a test_database the migrations fail as there are no entries in the test model and this data migration use .get()
I don't know how I should resolve this issue. The best way I could think of is my default database for testing.

Populating db data for django's built-in testing tools

I'm using Django(1.8) + DRF + uwsgi + nginx and trying to unit-test API that I've made. To run tests I need to populate db (create users, for example) and use this data in all tests. So I've tried two ways:
Create directly in TestCase.setUp:
class ApiTests(TestCase):
def setUp(self):
Account.objects.create_user(username='username', password='password')
Or use fixtures:
class ApiTests(TestCase):
fixtures = ['dump.json']
Next I run my project through supervisor:
system("service supervisord startall")
After everything is ready I try to access my API in test to login, using:
login_data = {"username": "username", "password": "password"}
rslt = client.post(HOST_NAME + '/login/', data=login_data)
... but I can't authorize, because users somehow don't exist in the db!
As I've found in the docs to Django tests, TestCase doesn't write data into db, but store it in a transaction, that is rolled back after testing. And as I can see I can get this data only on test-side (using User.objects.all() that is showing that users are created), but not on my nginx-server-side (User.objects.all() on this side shows 0 items).
ATM I can see few options:
Somehow force TestCase to commit data into db.
Populate data in other methods (but which?).
Use different testing libs.
Could you please help?
You shouldn't use web server to test django views even though in reality views do need to be driven by web servers. In order to test request/response behavior, you should use django test client. Django doc has excellent example about that, quoting:
>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
200
>>> response = c.get('/customer/details/')
>>> response.content
b'<!DOCTYPE html...'
On top of that, the doc explains in details how to do GET, POST request and how to test view's response, etc. Hope that makes sense.
Yes! You've already identified the problem. Django TestCase executes in its own little insulated environment. It will create test database prefixed with test_ at the beginning of each suite run, and will execute each test in a transaction, so even if you start the the test suite, pause it after initialization, set the django config that supervisord points to to the test database, continue test execution you will still not see any data.
The reason you will not see any data in the example above is because the test has its own transaction, then when you make a request to your webserver, the webserver will open up a different transaction and will not be able to see the uncommitted data from your test.
To test your views (in my experiences) django TestCases and test clients, usually get you 95% there. They are very fast because each test is executed in a transaction, they expose a test client, that mimics a request (url routing, middleware, view loading, template processing, etc).
Using TestCase should faithfully test all your logic and database interactions, but it still leaves a gap of if supervisor, nginx, uwsgi, django app are functioning correctly. When you have extensive coverage using TestCase simple integration/smoke tests should suffice to verify the services above can communicate correctly. Ie bring up the stack, hit a status page that will test -> Supervisor -> Nginx -> uwsgi -> django -> DB -> back out.
There are def a lot of options for purely functional tests, but why test at a flaky , timely to maintain level when django provides you the tools to verify your application in a reliable, quick, easy to use manner?
If you need a server to be available to a browser for a browser based test, django provides LiveServerTestCase
If you need to write extensive functional tests I have found great success in exposing fixutre creation methods as API methods. This way your tests would be executed AGAINST a stack, an arbitrary stack, in this case it will be against a test stack you bring up locally, but since the tests are separate they could be executed against a QA or staging or even prod stack.

Django unittests - ImproperlyConfigured error

Im trying to write tests for my module. When i run:
python manage.py test my_module
Im getting message:
django.core.exceptions.ImproperlyConfigured: Please fill out the database NAME in the settings module before using the database.
I dont have any test yet, just BaseTest where im creating users, groups and assigning permissions.
Where could be the problem? Server normally works, configuration seems to be good. Do i need to define settings for test?
Ok. I think i know what was the problem :] I had lists with permissions stored in other module. So i were writting from module.perms import perms (normal python list). Its seems, that python is doing something more than just import that list from other module to my module. And that was the cause of failing.
Solution:
Surround code after list definition with:
if __name__ == "__main__":
...
Then everything should be working good.
Ok. I think i know what was the problem :] I had lists with permissions stored in other module. So i were writting from module.perms import perms (normal python list). Its seems, that python is doing something more than just import that list from other module to my module. And that was the cause of failing.
Solution: Surround code after list definition with:
if __name__ == "__main__":
...
Then everything should be working good.
You need to have your DATABASE setting in place in settings.py, and your app needs to be in installed apps. For example:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'database.sqlite',
python manage.py test doesn't use the NAME database defined in settings.py DATABASES. From the docs -
Tests that require a database (namely, model tests) will not use your
"real" (production) database. Separate, blank databases are created
for the tests.
Regardless of whether the tests pass or fail, the test databases are
destroyed when all the tests have been executed.
By default the test databases get their names by prepending test_ to
the value of the NAME settings for the databases defined in DATABASES.
When using the SQLite database engine the tests will by default use an
in-memory database (i.e., the database will be created in memory,
bypassing the filesystem entirely!). If you want to use a different
database name, specify TEST_NAME in the dictionary for any given
database in DATABASES.
To answer your question. You can define a test database with the TEST_NAME setting. But you shouldn't have to.
Here's another potential solution for people finding this through search:
I was trying to define my own TestRunner class inside my settings file, which meant it had to import DjangoTestSuiteRunner. This import at the top of the file caused the ImproperlyConfigured error. Moving the custom TestRunner class to a separate file resolved the issue.