148
  • I have a python-django application
  • I'm using the unit testing framework
  • The tests are arranged in the file "tests.py" in the module directory
  • I'm running the tests via ./manage.py test app

Now..

  • The tests.py file is getting rather large/complex/messy
  • I'd like to break tests.py up into smaller collections of tests...

How?

John Mee
  • 50,179
  • 34
  • 152
  • 186

13 Answers13

139

Note that this approach is no longer valid from Django 1.6, see this post.

You can create tests folder with ___init___.py inside (so that it becomes a package). Then you add your split test .py files there and import all of them in ___init___.py.

I.e: Substitute the test.py file with a module that looks and acts like the file:

Create a tests Directory under the app in question

app
app\models.py
app\views.py
app\tests
app\tests\__init__.py
app\tests\bananas.py
app\tests\apples.py

Import the submodules into app\tests\__init__.py:

from bananas import *
from apples import *

Now you can use ./manage.py as if they were all in a single file:

./manage.py test app.some_test_in_bananas
Community
  • 1
  • 1
Tomasz Zieliński
  • 16,136
  • 7
  • 59
  • 83
  • 1
    Doh. You meant create a 'tests' module under the application I'm testing; not a new application called tests. I get it now. Awesome. Thanks! – John Mee Jun 08 '11 at 04:16
  • @John: I can't recognize my answer anymore! :-) But you are completely right that it was too vague, even if correct - your examples make it clear, contrary to my original wording. – Tomasz Zieliński Jun 08 '11 at 11:06
  • 2
    @Tomasz.. Your words are still there - wholly intact. I just fleshed it out a little since you put me on the right track. – John Mee Jun 08 '11 at 23:35
  • @John: I wasn't angry at all if that's what you mean :) It was just funny to see my own answer in a bit different shape – Tomasz Zieliński Jun 09 '11 at 15:31
  • I have always been under the impression that this technique does not work with the regular django test runner as it uses __test__ and suite() to find the tests. – jMyles Jun 11 '12 at 18:26
  • 4
    @jMyles, if by "regular django test runner" you mean `python manage.py test myapp` then in fact this answer does work just fine. (just tried it) – Kirk Woll Jul 24 '12 at 21:08
  • @ArturSoler is it really no longer valid? [the docs for 1.10 describe this approach under 'advanced'](https://docs.djangoproject.com/en/1.10/topics/testing/advanced/#using-the-django-test-runner-to-test-reusable-applications) – John Mee Mar 08 '17 at 22:39
  • 1
    Yes, all you need to do is to convert your sub-test folder created to a package by adding a blank. ____init__.py. file. After this, you actually don't need to do any import in your ____init__.py. As long as the files named in the sub-folder begin with test*, Django will look for it and run the test methods in it. – Don Pels Jun 29 '21 at 08:43
  • In `__init__.py` I had to do `from .bananas import *` (add a dot before the name of the file) but it worked otherwise. – Florian Laplantif Nov 27 '21 at 19:20
59

The behavior has changed in Django 1.6, so there is no longer a need to create a package. Just name your files test*.py.

From Django 1.7 documentation

When you run your tests, the default behavior of the test utility is to find all the test cases (that is, subclasses of unittest.TestCase) in any file whose name begins with test, automatically build a test suite out of those test cases, and run that suite.

From Django 1.6 documentation,

Test discovery is based on the unittest module’s built-in test discovery. By default, this will discover tests in any file named “test*.py” under the current working directory.

Previous behavior, from Django 1.5 documentation:

When you run your tests, the default behavior of the test utility is to find all the test cases (that is, subclasses of unittest.TestCase) in models.py and tests.py, automatically build a test suite out of those test cases, and run that suite.

There is a second way to define the test suite for a module: if you define a function called suite() in either models.py or tests.py, the Django test runner will use that function to construct the test suite for that module. This follows the suggested organization for unit tests. See the Python documentation for more details on how to construct a complex test suite.

Sergey Orshanskiy
  • 6,794
  • 1
  • 46
  • 50
  • 8
    In django 2.6 it doesn't really discover anything… – LtWorf Nov 23 '15 at 16:35
  • 3
    Currently using Django 1.10, I wanted to put all my `test*.py` files in a folder called `tests` to keep the folder clean - this is possible, but you must run `./manage.py test app.tests` and all relative imports need to go up a level (`from .models` becomes `from ..models`). – Scott Stevens Feb 24 '17 at 13:36
  • Putting all files into a single folder `tests`, I start testing just with `python manage.py test`. Important, name folder, classes and methods with prefix test. – MasterControlProgram Apr 08 '21 at 22:17
29

The answer as stated by Tomasz is correct. However, it can become tedious to ensure that the imports in __init__.py match your file structure.

To automatically detect all tests in the folder you can add this in __init__.py:

import unittest

def suite():   
    return unittest.TestLoader().discover("appname.tests", pattern="*.py")

This will allow you to run ./manage.py test appname but won't handle running specific tests. To do that you can use this code (also in __init__.py):

import pkgutil
import unittest

for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
    module = loader.find_module(module_name).load_module(module_name)
    for name in dir(module):
        obj = getattr(module, name)
        if isinstance(obj, type) and issubclass(obj, unittest.case.TestCase):
            exec ('%s = obj' % obj.__name__)

Now you can run all your tests via manage.py test app or specific ones via manage.py test app.TestApples

Bryce Drennan
  • 699
  • 6
  • 9
  • Where do you place the second piece? – rh0dium Apr 17 '13 at 23:24
  • Both pieces go into `__init__.py` – Bryce Drennan Apr 19 '13 at 20:04
  • Note that if any of your test package names coincide with top level module names which get imported during the test run, the pkgutil snippet will cause the import to fail because the tests get added as `sys.modules[packagename]`. A quick workaround is to `del` any that cause problems after the above. (Or you could rename your folders ;) ) – Paul Fenney Oct 30 '13 at 12:08
  • This is great but I ran into an error where, when running an app level test (`python manage.py test appName`) the second bit of code would throw an error stating that `__path__` was not available. I avoided it by wrapping the second snippet in a `if '__path__' in locals():` check, which did the trick. Thanks for the answer! – alukach Dec 19 '13 at 20:07
  • 1
    +1 this also ensures that the __init__ file adheres to common coding standards, i.e. does not have * or unused imports – Martin B. Oct 07 '14 at 15:26
  • might also just do `issubclass(obj, unittest.TestCase)` for Python 2.6 compatibilty – Martin B. Oct 07 '14 at 15:55
24

Just make your directory structure like this:

myapp/
    __init__.py
    tests/
        __init__.py
        test_one.py
        test_two.py
        ...
    ...

And python manage.py test myapp will work as expected.

spiderlama
  • 1,539
  • 15
  • 10
5

http://docs.python.org/library/unittest.html#organizing-tests talks about splitting the files into modules, and the section right above it has an example.

Jim Deville
  • 10,632
  • 1
  • 37
  • 47
  • 2
    The 'bit extra' I'm looking for, over rtfm, is the django settings environment, database, and fixtures for the tests. – John Mee Jun 08 '11 at 01:25
4

With Django 2.2 a simple and fairly good solution could be to create a test folder inside an app, and you can put your related test_...py files into, just add __init__.py to the test folder.

supervacuo
  • 9,072
  • 2
  • 44
  • 61
Gabor
  • 352
  • 5
  • 14
3

No need to code anything in init. Just create a subdirectory in your app. Only requirement is not to call it tests* For exemple

app/
app/__init_.py
app/serializers.py
app/testing/
app/testing/__init__.py
app/testing/tests_serializers.py
MaxBlax360
  • 1,070
  • 1
  • 12
  • 15
  • 1
    Why can you not call it something that starts with "tests"? – Serp C Nov 15 '16 at 17:58
  • I'm using this answer with Django 1.11.4. Reasons to use it: (1) The file "app/testing/__init__.py" remains empty and (2) The command remains the basic "python manage.py test app" – rprasad Oct 11 '17 at 14:55
1

I have two files. One is tests.py and another is test_api.py. I can run these individually as below.

manage.py test companies.tests
manage.py test companies.test_api

Refer @osa's response about file naming convention.

Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
kta
  • 19,412
  • 7
  • 65
  • 47
1

If you have a more complicated setup, or don't want to use from ... import *-type statements, you can define a function called suite in your tests.py (or tests/__init__.py), which returns an instance of unittest.TestSuite.

Joel Cross
  • 1,400
  • 15
  • 22
0

I think ./manage.py test simply does running all the tests trick (in django >= 1.7).

If your organizing tests is about grouping and cherrypicking and you are fan of nose use django nose:

python manage.py test another.test:TestCase.test_method

If you know nose, then you know how to "wildcard" much nicer over all your files.

PS

It is just a better practice. Hope that helps. The answer was borrowed from here: Running a specific test case in Django when your app has a tests directory

Community
  • 1
  • 1
Yauhen Yakimovich
  • 13,635
  • 8
  • 60
  • 67
0

In django you can use below comman or can check documentation. Also using this command will pick up files with pattern you provide not just test*.py or test_*.py.

Documentation
You can specify a custom filename pattern match using the -p (or --pattern) option, if your test files are named differently from the test*.py pattern:

$ ./manage.py test --pattern="tests_*.py"
Himanshu Patel
  • 568
  • 6
  • 14
0

Just create different test files with tests_name in your app

Say you have following test files:

tests_admins.py
tests_staff.py
tests_others.py
# will run both test files
(venv)..yourapp$./manage.py test --keepdb -v 2 appname
7guyo
  • 3,047
  • 1
  • 30
  • 31
0

Or in Windows, if you do not want to create a package (i.e folder with __init__.py) and just want to create a folder called "Tests" and this folder contains the test files then to run tests in cmd just enter

python manage.py test your_app_name/Tests

Since a path is expected