How to test single application (not project) in Django? - django

I would like to test my small application, that I keep in a separate package. Right now I created a "test_project" directory, created a test project there and I am using the project's manage.py to run tests. But I keep wondering - is there a better method? Is it possible to launch a single app's tests, perhaps with some default configuration (like, sqlite database)?

It is possible to run a single app's tests standalone, without creating a Django test project for that purpose. One way of doing so is by creating a runtests.py in your app's root dir which setups Django settings and runs ./manage.py test your_app programmatically. One example of runtests.py is Django's own: runtests.py (documentation).
Django's own runtests.py is a good reference but I find it convoluted for most cases. Below are a couple of alternative examples:
Django-Modeltranslation
My own minimalistic one

Writing this down because I don't see it on here yet
From the docs
If your django structure looks something like this as it should:
Project dir/
/myapp
/myotherapp
/myproject
Django allows you to execute test from only "myotherapp" by executing:
./manage.py test myotherapp/
This will execute all test under "myotherapp" whereas
./manage.py test
will execute all test in your projects directory.

If you use nose you can run a single app's tests.
python manage.py test app.tests:TestClassHere
as for testing a single app. I just follow the convention other big django projects do, and that is exactly what you are doing. Create a test_project directory.
https://github.com/mozilla/django-piston/tree/master/tests/test_project
I figure if it is good enough for some of the biggest django pluggable apps it's good enough for me.

Ofcourse you can test the Python parts with a standalone unittest if you don't need any django dependencies
A Django environment requires atleast a settings.py and manage.py file. You can setup those with essential stuff only and UnitTest your app with manage.py. You should see a project as a Django runtime environment, there's no Django without it. You can probably mimic settings but tbh you would just be creating more hacks than simplicity.

You can also create a custom test runner and override the path it searches for tests in.
Perhaps I buried the lede here. I recently inherited a project that had legacy tests they wanted to keep for historical reasons, so I was forced to write all my tests in one folder and point to it
from django.test.runner import DiscoverRunner
class MyTestRunner(DiscoverRunner):
def __init__(self, *args, **kwargs):
kwargs.update({'top_level': str(settings.TEST_DISCOVER_TOP_LEVEL)})
super().__init__(*args, **kwargs)
def run_tests(self, test_labels, extra_tests=None, **kwargs):
# In order to override the top_level directory, the test_label must match the top_level
kwargs.update({'test_labels': [str(settings.TEST_DISCOVER_TOP_LEVEL)]})
return super().run_tests(extra_tests=extra_tests, **kwargs)
and in your settings
TEST_RUNNER = 'path.to.my.module.MyTestRunner'
TEST_DISCOVER_TOP_LEVEL = BASE_DIR / 'tests/unit_and_integration_tests'

Related

Django single-test migration

I have recently implemented a reusable app within the django project I am working on. For the sake of the question, let's call it reusable_app. This app also has some unittests that run, however, these tests depend on some basic models declared somewhere next to the tests in a model.py.
/resuable_app
__init__.py
models.py
views.py
urls.py
/tests
__init__.py
tests.py
/simple_app
__init__.py
models.py
Now, the models aren't loaded in the database unless I specify the folder in INSTALLED_APPS in the testing configuration file. I was wondering if there is another way to achieve this, not having to expose the app in the settings file? I seem to be able to specify the app via #override_settings, but the migrations are not ran.
Ex:
#override_settings(INSTALLED_APPS=['reusable_app'])
class TestReusableApp(TestCase):
def test_something(self):
...
If reusable_app is not specified in the settings module INSTALLED_APPS this still yields a ProgrammingError. Am I missing something or is there another approach?
I think the issue here is that the test runner is setting up the tables before you add the app with #override_settings.
Normally what I do with reusable apps is to run the tests in the context of an "example" app, with settings that include the app your want to test. Usually works pretty well, as I'm packaging the reusable app separately. Here's an example of this from a past project of mine.
However, if that's not possible, you might try to override setUp in your tests, and call the "migrate" command within that code. For example:
from django.core.management import call_command
#override_settings(INSTALLED_APPS=['reusable_app'])
MyTestCase(TestCase):
def setUp(self):
call_command('migrate', 'reusable_app')
This is a bit messy, but it might be worth trying. Depending on how things go, you might also have to run django.setup().

Django Test - How to run tests with different settings file for a specific app?

I have a project and into this project I have different apps.. The problem is, a specific app is another website inside the same project and uses different settings file.
When I run the tests separated, it works perfectly, when I try to run all tests together, the app which uses a different settings file fails..
Any idea how to fix it?
I tried to override the settings into setUp method as below:
for s in dir(settings_appA):
if getattr(settings, s) == None:
setattr(settings, s, getattr(settings_appA, s))
When I do it, works for the specific website but not for other apps..
Any idea?
./manage.py test <app1> --settings=settings1
./manage.py test <app2> --settings=settings2

Django separate DB engine for unit tests

I am new to Django and following several tutorials. I created a separate app for functional testing. I also setup a postgres DB engine for my prod DBMS but for testing I'd like to use SQLite3 in memory. Could someone tell me how to override the prod DB with another settings.py file? Where would I put the override file, in my functional test app folder? How does Django know which settings to use for prod vs. testing? Any help appreciated, thanks!
As an option:
Create a file names settings_test.py that contains something like:
from settings import * # if your main settings file is settings.py
DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'
# and whatever other settings like db name etc.
Then run tests with the settings:
./manage.py test --settings=settings_test
Complementing Dima's answer, you can take a look at:
https://docs.djangoproject.com/en/1.4/internals/contributing/writing-code/unit-tests/#using-another-settings-module

what magic is "django-admin startapp" doing, that the test runner needs to find my tests.py

I was having a problem that the django test runner wasn't finding the tests for my app, like this one:
Django test runner not finding tests
One of the comments on that thread suggested creating a new app with django-admin.py and seeing if the tests ran there. e.g.
django-admin.py startapp delme
then
adding "delme" to my INSTALLED_APPS
then
copying my tests.py from the app where it wasn't getting found into delme/
and viola! the tests did run. So, OK, I have a work arround, but I don't understand. I re-read what I think should be the relevant parts of the django documentation but the penny refuses to drop.
BTW, the app works via runserver and wsgi, so there doesn't appear to be any gross configuration problem. And my tests all pass from their new home, so I obviously need more tests :)
Specifically, I'm running django in a virtualenv, so I had run "django-admin.py startapp" in the (activated) virtualenv where I wanted the tests to run. This doesn't make the tests run in my other virtualenvs, I still have the old symptoms there (Ran 0 tests). I have a multitude of virtualenvs, managed by non-trivial paver scripts. One uses "path.copytree" for deploying projects, rewrites apache config files, restarts apache, writes wsgi files using the appropriate virtualenv, etc. The other uses PIP/GCC/aptitude/etc for bootstrapping/tearing down the different environments, updating packages as per configuration, etc. So I want to understand the difference between django-startapp and simply copying files, so I can fix these paver scripts so the tests can run in any environment I want them to.
The only thing that makes sense to me, after reading your description, is the location of paths for your existing apps. Can you confirm the following things:
Your app is at the same folder level as the delme app
Your app folder contains an __init__.py file
Your app is listed in the INSTALLED_APPS setting
I'm going to guess that it's a missing __init__.py file, as that trips some people up. To answer your specific question, django-admin startapp doesn't do anything magical. It just creates the right folders and files in the correct place.
Your folder structure should be...
my_project/
__init__.py
manage.py
settings.py
my_app/
__init__.py
tests.py
models.py
delme/
__init__.py
tests.py
models.py
Also note this comment
You can't easily name the TestCase class directly. You name the app, the Django runner does it's discovery thing by looking in models.py and tests.py
solved (still with a little whiff of magic).
diff showed that my old app didn't have a models.py but the new app ("delme", working) did. I didn't think the old app needed one, it was importing all it's domain classes from other places.
Touching an empty models.py in my old app fixed it, now the test runner finds the tests.py and everything works as expected. Condlusion - if an app has no models.py, the django test runner won't find the app's tests.py.
What I said about not working in different virtualenvs was bogus (red herring), I was a bit confused about what my deploy scripts were doing.

How to do a project-wide custom django-admin command?

In Django, it's easy to write custom management commands for django-admin.py/manager.py.
However, these commands reside at the application level, not the overall project level. You put implementations into the <project_dir>/<app_dir>/management/commands directory, and they get auto-discovered.
I have a number of commands which are project-level, not tied to any one application in the project. I can tuck them in one of the apps, but is there a way to implement project-level management commands in Django?
You can have application called core or similar, where things not tied to any application are contained. These can be management commands, temlatetags, models and maybe other modules like forms, decorators, middleware. You could use your project directory itself as "core" application.
Here is how I tend to structure my projects:
project_name
not_reusable_app1
not_reusable_app2
templatetages
tempates
utils
models.py
settings.py
management
middleware.py
forms.py
processors.py
__init__.py
parts
reusable-app-1
reusable_app_1
setup.py
reusable-app-2
reurable-app-3
gereric-python-lib
django
setup.py
My INSTALLED_APPS usually looks like this:
INSTALLED_APPS = (
...
'project_name',
'project_name.not_reusable_app1',
'reusable_app1',
...
)
I do not give any special treatment to django applications compred with other python packages. For example I don't put them under apps or similar directory.
It is clear that my not-reusable apps are part of project. Not reusable apps under project usually use various utilities from project, for example project_name.utils.decorators.some_kind_of_deco.
If you do not like to use project as application, like I mentioned you can move everything to project_name.core.