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?
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
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.
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
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.
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.
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.
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
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.
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.
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
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"
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
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
Related
I am new to Odoo. I am using Odoo 10. I would like to write test cases for a new wizard I created under a module A. I put all my wizard code (views + models) inside wizards directory. I created unit test cases under the path <<module/tests>> followed all the file/class/method naming conventions. When I try to upgrade the module (with test enable) to run unit test cases, all the other modules tests scripts are run but not for the newly created module A. Please suggest what additional changes might be needed to enable test scripts for a newly created module with wizard.
Thank you.
I believe that the structure you have follow is according to the standard way. You can find the structure here Testing Module Structure.
Also please check that the naming on the folder and file in which you wrote the code,
for example - tests/test_todo.py Also, don't forget to update the import in tests/init.py to from. import test_todo
This is because Odoo expects the test module names to start with test_ when it searches for tests belonging to a module Code Reference
Command to Run the testcases:
python ./odoo.py -i module_to_test --log-level=test -d your_database --db-filter=your_database --test-enable --stop-after-init
I'm building a Django project with Python 3.6. I have created this directory structure ...
project
- manage.py
- scripts
- run_commands.py
- commons
- util
- __init__.py
- my_class.py
The contents of init.py are
from . import my_class
In another class, I attempt to import my MyClass like this
from commons.util import MyClass
but I'm getting this error
ModuleNotFoundError: No module named 'commons'
Am I creating my init.py properly?
It looks like the problem is that MyClass is not located in commons.util, because you only imported the module named my_class, not the class itself.
Instead the file commons/util/__init__.py should contain the following import:
from .my_class import MyClass
I don't think this will solve your problem, because you would be getting a different error than the one shown, but you will get errors for this eventually.
Update
First, I'd recommend reading this answer for a good explanation for how imports work in python.
Basically, when you execute from commons.util import MyClass, the interpreter scans the contents of sys.path for a module named commons.
I assume you didn't set sys.path to include your project folder, hence the ModuleNotFoundError.
TLDR; you have 2 options:
Manually set sys.path in run_commands.py to check your project folder (Don't do this!)
Use Django's Command class
To use Django's Command class, you will need to adjust your project folder similar to the following:
project
- manage.py
- commons
- management
- commands
run_commands.py
- util
- __init__.py
- my_class.py
Now in run_commands.py:
from django.core.management.base import BaseCommand
from commons.util import MyClass
class Command(BaseCommand):
def handle(*args, **options):
print("handling", MyClass)
You can execute the new command with the following:
python3 manage.py run_commands
It used to be the case that yes, you need to put an __init__.py in every directory that is going to be treated as a python module as without an __init__.py python wouldn't look inside that directory for importable code.
- project
- __init__.py
- commons
- __init__.py
- util
- __init__.py
- my_class.py
But as Reinstate Monica points out below this is no longer true as of Python 3.3+. So, depending on your version of Python you will need to make an informed decision.
Note, you might or might not need an __init__.py in the root project directory (if you need them at all), it depends if it has definitions that are part of the source tree. But you won't need it if it's just a container, like you see in the way most Django projects are organised, for example.
https://docs.python.org/3/tutorial/modules.html
https://docs.python.org/3/tutorial/modules.html#packages
I just started a python project and I'm trying out different test frameworks.
The problem I have is that nose2 does not find my tests:
$ nose2 --verbose
Ran 0 tests in 0.000s
OK
while nosetests find them all
$ nosetests --collect-only
.................................
Ran 33 tests in 0.004s
OK
Otherwhise I can execute a single test with nose2 from same directory:
$ nose2 myproj.client.test.mypkg.mymodule_test
.
Ran 1 test in 0.007s
OK
where myproj.client.test.mypkg.mymodule_test is like:
'''
Created on 18/04/2013
#author: julia
'''
from unittest import TestCase, main
import os
from myproj.client.mymodule import SUT
from mock import Mock
import tempfile
class SUTTest(TestCase):
def setUp(self):
self.folder = tempfile.mkdtemp(suffix='myproj')
self.sut = SUT(self.folder, Mock())
self.sut.init()
def test_wsName(self):
myfolder = os.path.join(self.folder, 'myfolder')
os.mkdir(myfolder)
self.sut.change_dir(myfolder)
self.assertEquals(self.SUT.name, 'myfolder')
if __name__ == "__main__":
main()
I've been looking at documentation and I cannot find a possible cause for this.
Running python 2.7.3 on MacOs 10.8.3
Adding to MichaelJCox's answer, another problem is that nose2, by default, is looking for your test file names to begin with 'test'. In other words, 'testFilePattern == test*.py' (you can find that in nose2/session.py).
You can fix this in two ways:
Specify a different test file pattern in a configuration file:
Create a configuration file somewhere in your project (the base directory is a good place, or wherever you will run nose2). nose2 will look for and load any file called nose2.cfg or unittest.cfg.
Add this to that configuration file.
[unittest]
test-file-pattern=*.py
Now run nose2 again and it'll find those old test cases. I'm unsure if this could adversely affect nose2 performance or what, but so far so good for me.
Rename your test files so that they begin with test.
For example, if you have a project like this:
/tests/
__init__.py
fluxcapacitor.py
Rename /tests/fluxcapacitor.py to /tests/test_fluxcapacitor.py, now nose2 will find the tests inside fluxcapacitor.py again.
More verbose output
Finally, this is unrelated to your question but might be helpful in the future: If -verbose doesn't output enough info, you can also pass the following additional arg --log-level debug for even more output.
It looks like nose2 needs 1 of 3 things to find the test:
Your tests need to be in packages (just create __init__.py files in each dir of your test structure)
You need a directory named 'test' in the same directory in which nose2 is being run
It needs to be in the same directory
nose2's _discovery method (in nose2.plugins.loader.discovery.py) is explicitly looking for directories named 'test' or directories that are packages (if it doesn't just pick up your test files from the same directory):
if ('test' in path.lower()
or util.ispackage(entry_path)
or path in self.session.libDirs):
for test in self._find_tests(event, entry_path, top_level):
yield test
If I set up a similar test file (called tests.py) and run nose2 in the same directory, it gives me the 1 test OK back.
If I create a directory named 'test', move the file to it, and run nose2 from the top directory, I get an error stating that it can't import my py file. Creating an __init__.py in that directory fixes that error.
If I make a directory 'blah' instead and move the file there, then I see the issue you list above:
Ran 0 tests in 0.000s
OK
However, if I then create an __init__.py in directory 'blah', the test runs and I get my 1 test found and OK'd.
I'm having trouble creating a test suite in Django 1.3.
Say I have an installed app in a directory called app_name. One of the files in that directory is foo.py which defines a class named Foo. I want to test that, so I also have a file that directory called foo_test.py which defines a class named FooTest. That file looks like:
import unittest
import foo
class FooTest(unittest.TestCase):
def setUp(self):
self.foo_instance = foo.Foo()
... etc
Now down the line I'll have other test cases in other files, and I'll want to run them all as part of a test suite. So in the same directory app_name I created a file tests.py which will define the suite. At first I defined it like:
import foo_test
from django.test.simple import DjangoTestSuiteRunner
def suite():
runner = DjangoTestSuiteRunner()
return runner.build_suite(['app_name'])
Unfortunately, this fails because calling runner.build_suite(['app_name']) searches app_name for a tests.py file, executes suite(), and this continues recursively until the Python interpreter stops everything for exceeding the maximum recursion depth.
Changing runner.build_suite(['app_name']) to
runner.build_suite(['app_name.foo_test'])
or
runner.build_suite(['app_name.foo_test.FooTest'])
leads to errors like ValueError: Test label 'app_name.foo_test' does not refer to a test.
And changing it to:
runner.build_suite(['foo_test'])
or
runner.build_suite(['foo_test.FooTest'])
leads to errors like App with label foo_test could not be found.
I'm kind of out of ideas at this point. Any help would be very much appreciated. Thanks!
See the Python documentation for organizing tests, and use one of the alternative methods there to build your test suite. Incidentally, none of the recommended methods employ build_suite.
I have replaced project/app/tests.py with a project/app/tests/ directory. The directory contains several Python files (call them apples.py, bananas.py, etc.), each of which defines one or more classes derived from TestCase (call them TestApples, TestBananas, etc.). The file project/app/tests/__init__.py contains
from apples import TestApples
from bananas import TestBananas
The command manage.py test app still works, but manage.py test app.bananas and manage.py test app.tests.bananas do not, e.g.:
ValueError: Test label 'app.bananas' does not refer to a test
manage.py test app.tests.bananas fails with the same error, but manage.py test app.tests.bananas.TestBananas is more hopeful:
ValueError: Test label 'store.tests.bananas.TestBananas' should be of the form app.TestCase or app.TestCase.test_method
The Django docs and Python docs suggest that the solution is to write a custom test runner or test collector and plug it in; this StackOverflow question goes down the same route, then seems to recommend switching to django-nose. I'd rather not unless I have to, and I'm curious to see how to make this work with Django's standard tools. Anyone have a simple(ish) solution?
In your example, if you run manage.py test app.TestBananas then you can run that specific test.
You can get everything working by making sure all your tests are imported into __init__.py but when you have lots of tests this becomes difficult to manage. If you want to run the tests in PyCharm then django-nose isn't an option.
To make this easier we can have the test suite automatically find all tests in the tests package. Just put this in __init__.py (Be sure to replace "appname"):
def suite():
return unittest.TestLoader().discover("appname.tests", pattern="*.py")
This still won't allow us to run specific tests. To do that you'll need to add this code at the top of __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
I just do this tutorial.
Edit: after django 1.6, the test discovery mechanism changed. You just have to create a folder tests with an __init__.py file inside, and put your test files there.
Your test files should match test*.py pattern.
I use django-nose! Don't be scared of packages.
Did you try renaming your test files to "test_foo.py", instead of "foo.py", for example?