Why coverage doesn't report anything on Djangos views.py? - django

I have a class based view in a Django app that looks something like this :
class CBView(View):
def get(self, request, client, *args, **kwargs):
output1 = self.method1(argument1)
output2 = self.method2(argument2)
# Rest of the method implementation
...
return response
def method1(self, argument1):
# Implementation
...
return output1
def method2(self, argument2):
# Implementation
...
return output2
And I'm trying to write unit tests for the 'easy' class methods, namely method1 and method2. The tests looks like this :
class TestCBView(TestCase):
def setUp(self):
self.view = CBView()
def test_method1(self):
# Testing that output1 is as expected
...
output1 = self.view.method1(argument1)
...
self.assertEquals(output1, expected_output1)
def test_method2(self):
# Testing that output2 is as expected
...
output2 = self.view.method2(argument2)
...
self.assertEquals(output2, expected_output2)
After that, I run:
coverage run ./manage.py test django_app.tests.test_cbview
Which runs all the tests successfully, then I try to run:
coverage report -m django_app/views.py
And I get :
Name Stmts Miss Cover Missing
-------------------------------------
No data to report.
Am I doing something wrong ?
I'm using Coverage.py, version 4.0.3., Django 1.8.15 and Python 2.7.13.

I just had the same problem. It appeared I had some not migrated data, so after I ran in the terminal
python manage.py makemigrations
python manage.py migrate
and I tried again with
coverage html --include=django_app/views.py
coverage run manage.py test django_app.tests
everything was already fine.

When running tests with coverage it generates the .coverage file which is in a private format and not intended to be read directly. This file contains the raw reports which will be used to show the report for you, either on console or in html format with coverage html. So normally if the tests were ran, the .coverage file must be there in the directory from which you launched the command coverage run ./manage.py test django_app.tests.test_cbview and then being under that directory, you can hit coverage report ... and should work.

Related

scope="module" does not seem to be working in pytest

I am working with pytest fixtures. My test module is as follows :
import pytest
#pytest.yield_fixture
#pytest.fixture(scope="module")
def jlt() :
print("setup executed")
yield None
print("tearing up")
def test_one(jlt) :
id = 123
assert id == 123
def test_two(jlt) :
id = 456
assert id == 456
I am executing this as follows :
py.test -v --capture=no test_jlt.py
The output is :
platform linux2 -- Python 2.7.12, pytest-2.8.7, py-1.4.31, pluggy-0.3.1 -- /usr/bin/python
cachedir: ../../.cache
rootdir: /home/vandana/unix Home/pythonLearn, inifile: pytest.ini
collected 2 items
test_jlt.py::test_one setup executed
PASSEDtearing up
test_jlt.py::test_two setup executed
PASSEDtearing up
The scope="module" does not seem to be working. The fixture is getting executed for each function and not just once for the entire module.
I do not know what should be done
#pytest.yield_fixture replaces #pytest.fixture, so you should use #pytest.yield_fixture(scope="module") instead.
Note that with pytest 3.x you can simply use #pytest.fixture and use yield inside the fixture, which simplifies things a bit.

Test different invocation pattern

I want to test how a project generated by cookiecutter behave with multiple invocation patterns.
Given the following generated project
proj/
proj/
__init__.py
__main__.py
Content of __init__.py:
def func():
pass
Content of __main__.py:
from proj import func
def main():
func()
if __name__ == '__main__':
main()
(I have read about issues of __main__.__spec__, this is not what this question is about. In fact, I would like to test-drive -TDD- my work arounds of these issues)
Now I write tests in which the generated project structure and location is known (available via pytest fixtures, for instance)
test_run_proj_script():
"""Test behavior of ``python3 ./proj``"""
pass # how to run this?
test_run_proj_module():
"""Test behavior of ``python3 -m ./proj``"""
pass # how to run this?
Bonus: inject values for sys.argv ?

Why are my tests not getting run in my TestCase subclass?

I am learning test driven development...
I wrote a test that should fail but it's not...
(env)glitch:ipals nathann$ ./manage.py test npage/
Creating test database for alias 'default'...
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
Destroying test database for alias 'default'...
in npage/ I have tests.py:
from django.test import TestCase
from npage.models import Tip
import datetime
# Example
class TipTester(TestCase):
def setUp(self):
print dir(self)
Tip.objects.create(pk=1,
text='Testing',
es_text='Probando')
def tips_in_spanish(self):
my_tip = Tip.objects.get(pk=1)
my_tip.set_language('es')
self.assertEqual(my_tip.text, 'this does not just say \'Probando\'')
What am I doing wrong? I've read this but I still can't figure out what is going wrong here.
Your test functions need to start with test:
def test_tips_in_spanish(self):
Docs here
"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."

How to test custom django-admin commands

I created custom django-admin commands
But, I don't know how to test it in standard django tests
If you're using some coverage tool it would be good to call it from the code with:
from django.core.management import call_command
from django.test import TestCase
class CommandsTestCase(TestCase):
def test_mycommand(self):
" Test my custom command."
args = []
opts = {}
call_command('mycommand', *args, **opts)
# Some Asserts.
From the official documentation
Management commands can be tested with the call_command() function. The output can be redirected into a StringIO instance
You should make your actual command script the minimum possible, so that it just calls a function elsewhere. The function can then be tested via unit tests or doctests as normal.
you can see in github.com example
see here
def test_command_style(self):
out = StringIO()
management.call_command('dance', style='Jive', stdout=out)
self.assertEquals(out.getvalue(),
"I don't feel like dancing Jive.")
To add to what has already been posted here. If your django-admin command passes a file as parameter, you could do something like this:
from django.test import TestCase
from django.core.management import call_command
from io import StringIO
import os
class CommandTestCase(TestCase):
def test_command_import(self):
out = StringIO()
call_command(
'my_command', os.path.join('path/to/file', 'my_file.txt'),
stdout=out
)
self.assertIn(
'Expected Value',
out.getvalue()
)
This works when your django-command is used in a manner like this:
$ python manage.py my_command my_file.txt
A simple alternative to parsing stdout is to make your management command exit with an error code if it doesn't run successfully, for example using sys.exit(1).
You can catch this in a test with:
with self.assertRaises(SystemExit):
call_command('mycommand')
I agree with Daniel that the actual command script should do the minimum possible but you can also test it directly in a Django unit test using os.popen4.
From within your unit test you can have a command like
fin, fout = os.popen4('python manage.py yourcommand')
result = fout.read()
You can then analyze the contents of result to test whether your Django command was successful.

Configure Django to find all doctests in all modules?

If I run the following command:
>python manage.py test
Django looks at tests.py in my application, and runs any doctests or unit tests in that file. It also looks at the __ test __ dictionary for extra tests to run. So I can link doctests from other modules like so:
#tests.py
from myapp.module1 import _function1, _function2
__test__ = {
"_function1": _function1,
"_function2": _function2
}
If I want to include more doctests, is there an easier way than enumerating them all in this dictionary? Ideally, I just want to have Django find all doctests in all modules in the myapp application.
Is there some kind of reflection hack that would get me where I want to be?
I solved this for myself a while ago:
apps = settings.INSTALLED_APPS
for app in apps:
try:
a = app + '.test'
__import__(a)
m = sys.modules[a]
except ImportError: #no test jobs for this module, continue to next one
continue
#run your test using the imported module m
This allowed me to put per-module tests in their own test.py file, so they didn't get mixed up with the rest of my application code. It would be easy to modify this to just look for doc tests in each of your modules and run them if it found them.
Use django-nose since nose automatically find all tests recursivelly.
Here're key elements of solution:
tests.py:
def find_modules(package):
"""Return list of imported modules from given package"""
files = [re.sub('\.py$', '', f) for f in os.listdir(os.path.dirname(package.__file__))
if f.endswith(".py") and os.path.basename(f) not in ('__init__.py', 'test.py')]
return [imp.load_module(file, *imp.find_module(file, package.__path__)) for file in files]
def suite(package=None):
"""Assemble test suite for Django default test loader"""
if not package: package = myapp.tests # Default argument required for Django test runner
return unittest.TestSuite([doctest.DocTestSuite(m) for m in find_modules(package)])
To add recursion use os.walk() to traverse module tree and find python packages.
Thanks to Alex and Paul. This is what I came up with:
# tests.py
import sys, settings, re, os, doctest, unittest, imp
# import your base Django project
import myapp
# Django already runs these, don't include them again
ALREADY_RUN = ['tests.py', 'models.py']
def find_untested_modules(package):
""" Gets all modules not already included in Django's test suite """
files = [re.sub('\.py$', '', f)
for f in os.listdir(os.path.dirname(package.__file__))
if f.endswith(".py")
and os.path.basename(f) not in ALREADY_RUN]
return [imp.load_module(file, *imp.find_module(file, package.__path__))
for file in files]
def modules_callables(module):
return [m for m in dir(module) if callable(getattr(module, m))]
def has_doctest(docstring):
return ">>>" in docstring
__test__ = {}
for module in find_untested_modules(myapp.module1):
for method in modules_callables(module):
docstring = str(getattr(module, method).__doc__)
if has_doctest(docstring):
print "Found doctest(s) " + module.__name__ + "." + method
# import the method itself, so doctest can find it
_temp = __import__(module.__name__, globals(), locals(), [method])
locals()[method] = getattr(_temp, method)
# Django looks in __test__ for doctests to run
__test__[method] = getattr(module, method)
I'm not up to speed on Djano's testing, but as I understand it uses automatic unittest discovery, just like python -m unittest discover and Nose.
If so, just put the following file somewhere the discovery will find it (usually just a matter of naming it test_doctest.py or similar).
Change your_package to the package to test. All modules (including subpackages) will be doctested.
import doctest
import pkgutil
import your_package as root_package
def load_tests(loader, tests, ignore):
modules = pkgutil.walk_packages(root_package.__path__, root_package.__name__ + '.')
for _, module_name, _ in modules:
try:
suite = doctest.DocTestSuite(module_name)
except ValueError:
# Presumably a "no docstrings" error. That's OK.
pass
else:
tests.addTests(suite)
return tests