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.
Related
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
I am writing a Django management command that visits a few pages, logged in as a superuser, and saves the results to a set of .html files.
Right now I'm just using the requests library and running the command with the development server running. Is there an easy way to generate the HTML from a view response so I do without actual HTTP requests?
I could create a request object from scratch but that seems like more overhead than the current solution. I was hoping for something simple.
Django has a RequestFactory which seems to suit your needs.
While it's not exactly meant for this purpose, an option would be to use the testing framework's Client to fake a request to the url - be sure to use client.login() before making your requests, to ensure you have superuser capabilities.
I'm developing sites on django I'm think what most problems may be found by using smoke coverage tests method. But (in most cases) write the tests to check response code is 200 for every app, every view, and every url is so bored (e.g. when you are develop few sites parallel). I have a question: How can I automate this process, may be exist some complete solutions to generate some common tests for django.
Thanks!
Best practices:
If it can break, it should be tested. This includes models, views,
forms, templates, validators, and so forth.
Each test should generally only test one function.
Keep it simple. You do not want to have to write tests on top of
other tests. Run tests whenever code is PULLed or PUSHed from the
repo and in the staging environment before PUSHing to production.
When upgrading to a newer version of Django:
-upgrade locally,
-run your test suite,
-fix bugs,
-PUSH to the repo and staging, and then
-test again in staging before shipping the code.
https://realpython.com/blog/python/testing-in-django-part-1-best-practices-and-examples/
Django provides a small set of tools that come in handy when writing tests.
The test client
The test client is a Python class that acts as a dummy Web browser, allowing you to test your views and interact with your Django-powered application programmatically.
Some of the things you can do with the test client are:
Simulate GET and POST requests on a URL and observe the response –
everything from low-level HTTP (result headers and status codes) to
page content.
See the chain of redirects (if any) and check the URL and status code
at each step.
Test that a given request is rendered by a given Django template,
with a template context that contains certain values.
Overview and a quick example
To use the test client, instantiate django.test.Client and retrieve Web pages:
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
'<!DOCTYPE html...'
As this example suggests, you can instantiate Client from within a session of the Python interactive interpreter.
Testing responses
The get() and post() methods both return a Response object. This Response object is not the same as the HttpResponse object returned by Django views; the test response object has some additional data useful for test code to verify.
Making requests
Use the django.test.Client class to make requests.
class Client(enforce_csrf_checks=False, **defaults)
Exceptions
If you point the test client at a view that raises an exception, that exception will be visible in the test case. You can then use a standard try ... except block or assertRaises() to test for exceptions.
Provided test case classes
Normal Python unit test classes extend a base class of unittest.TestCase. Django provides a few extensions of this base class:
Hierarchy of Django unit testing classes (TestCase subclasses)
Hierarchy of Django unit testing classes
For detailed information and more examples visit https://docs.djangoproject.com/en/1.7/topics/testing/tools/
So I'm doing some unittests on a particular Django app. When in a testing environment, I basically create a fresh database with my own data.
In a normal non-testing environment, I load up a page with a person's details. When this happens a signal is sent and it retrieves the person's picture (which is in a different app) and some other certain data (which is also in a different app).
So in my testing I have used fixtures to get all of the relevant data for the people who I create in the testing database. But I can't work out how to do this with the pictures...Can I create two lots of 'fixtures'?
The first test I am trying is ever so simple
resp = self.client.get(reverse('person_detail', kwargs={'id': 'blobby'}))
self.assertEqual(resp.status_code, 200)
So 'person_detail' is a named url I have and it successfully gets the "other data" using the fixtures, but I somehow need it to get a picture as my test(s) fail with the message 'Pic matching query does not exist'. Even just giving all of my test database people a default picture would be great. Anyone got any ideas about how to approach this? The pictures are saved on file...
In your TestCase class, include a setUp() function that pulls in the pictures for everyone. (https://docs.djangoproject.com/en/dev/topics/testing/overview/)
Using Selenium within Django I'm trying to run 2 acceptance tests on my django app. The first test registers a new user of my app, the second one logs that user in to the system and tries to perform an action. However, when running the tests, between the first and second tests, the user is erased from the database. It seems the database is wiped between each test. All the documentation I have seen indicates this isn't supposed to be happening.
How can I prevent this from occurring?
Note: Both the setUpClass and tearDownClass methods of my Test Class have #classmethod decorators. Also, the browser doesn't close between the two tests running.