Coverage wrongly reports for Django rest framework

Viewed 695

I tried to create simple api with Django Rest Framework and use structure like in book Two Scopes of Django 3.x. I tried to create simple test and coverage reports almost 100% coverage on all python sources in my app. Even if I completely remove that test. I tried pytest, nose, and anything what I found here and still same results.

My project structure is following:

.
├── api
│   ├── apps.py
│   ├── __init__.py
│   ├── urls.py
│   └── v1
│       ├── admin.py
│       ├── __init__.py
│       ├── migrations
│       ├── models.py
│       ├── serializers.py
│       ├── tests
│       │   ├── __init__.py
│       │   ├── test_api.py
│       │   ├── test_models.py
│       │   ├── test_serializers.py
│       │   └── test_views.py
│       ├── urls.py
│       └── views.py
├── config
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings
│   │   ├── base.py
│   │   ├── dev.py
│   │   ├── __init__.py
│   │   ├── production.py
│   │   └── test.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── README.md
├── requirements
│   ├── base.txt
│   ├── dev.txt
│   ├── production.txt
│   └── test.txt
└── venv

I also tried to modify manage.py with following changes:

    is_testing = 'test' in sys.argv
    if is_testing:
        import coverage
        cov = coverage.coverage(source=['api'], omit=['*/tests/*'])
        cov.erase()
        cov.start()
    execute_from_command_line(sys.argv)

    if is_testing:
        cov.stop()
        cov.save()
        cov.report()

but this also not helped.

here is an example result of coverage:

(venv) ➜  project ./manage.py test
System check identified no issues (0 silenced).

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK
Name                                Stmts   Miss  Cover
-------------------------------------------------------
api/__init__.py                         0      0   100%
api/apps.py                             3      3     0%
api/urls.py                             2      0   100%
api/v1/__init__.py                      0      0   100%
api/v1/admin.py                         4      0   100%
api/v1/migrations/0001_initial.py      10     10     0%
api/v1/migrations/__init__.py           0      0   100%
api/v1/models.py                       38      3    92%
api/v1/serializers.py                  23      0   100%
api/v1/urls.py                         10      0   100%
api/v1/views.py                        19      0   100%
-------------------------------------------------------
TOTAL                                 109     16    85%

and test is:

from django.urls import reverse
from rest_framework import status

from rest_framework.test import APITestCase
from rest_framework_simplejwt.tokens import RefreshToken

from api.v1.models import User


class BrandTests(APITestCase):
    def setUp(self) -> None:
        self.user = User.objects.create(username='testUser', password='testPass')
        self.token = RefreshToken.for_user(self.user).access_token
        self.client.credentials(HTTP_AUTHORIZATION='JWT ' + str(self.token))

    def test_create_brand(self):
        """
        Ensure that we can create brand
        """
        url = reverse('brand-list')
        response = self.client.get(url)
        self.assertEqual(status.HTTP_200_OK, response.status_code)

EDIT: I created simple app in django and I am getting same result. So it is not caused by project structure neither of combination django+djangorestframework

Does someone face to same issue?

2 Answers

Remember that importing a module will run all of the top-level statements of the module. So just importing a module will cover 100% of the import/class/def lines in that module. If you start with a nearly empty project, most of the lines in it will be those kinds of lines, and the coverage will be very high.

ignore what you don't want to be covered (or you think shouldn't such as migrations and settings. A .coveragerc can help you control what you want to be covered. a sample .coveragerc file might contain the following.

[run]
source = '.'
omit =
    *tests*
    *apps.py
    *manage.py
    *__init__.py
    *migrations*
    *asgi*
    *wsgi*
    *admin.py
    *urls.py
    */settings/*

[report]
omit =
    *tests*
    *apps.py
    *manage.py
    *__init__.py
    *migrations*
    *asgi*
    *wsgi*
    *admin.py
    *urls.py
    */settings/*

fail_under = 98
show_missing = True
skip_covered = True
Related