もた日記

くだらないことを真面目にやる

Djangoメモ(9) : ビューのテストを作成して実行する

Python 3.6.4 Django 2.0.2

A Complete Beginner's Guide to Djangoのチュートリアルを参考にビューのテストを作成して実行してみる。


テストを実行する

Djangoのテストはpython manage.py testコマンドで実行できる。現時点でテストは作成していないので当然何も実行されない。

$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).

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

OK
Destroying test database for alias 'default'...


ビューのテストを作成する

チュートリアル通りにboards/test.pyを編集。
このテストは前回作成したhomeのレスポンスのステータスコードが200 OKであるか確認するテスト。

変更前

from django.test import TestCase

# Create your tests here.

変更後

from django.core.urlresolvers import reverse
from django.test import TestCase

class HomeTests(TestCase):
    def test_home_view_status_code(self):
        url = reverse('home')
        response = self.client.get(url)
        self.assertEquals(response.status_code, 200)

編集後、実行したがエラーとなってしまった。

$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: boards.tests (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: boards.tests
Traceback (most recent call last):
  File "/home/vagrant/.pyenv/versions/3.6.4/lib/python3.6/unittest/loader.py", line 428, in _find_test_path
    module = self._get_module_from_name(name)
  File "/home/vagrant/.pyenv/versions/3.6.4/lib/python3.6/unittest/loader.py", line 369, in _get_module_from_name
    __import__(name)
  File "/home/vagrant/django/myproject/myproject/boards/tests.py", line 1, in <module>
    from django.core.urlresolvers import reverse
ModuleNotFoundError: No module named 'django.core.urlresolvers'


----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)
Destroying test database for alias 'default'...

調べたところ、ビューからURLに変換してくれるreverse()関数はDjango 2.0ではdjango.urlsに変わったようなので修正する。

# from django.core.urlresolvers import reverse
from django.urls import reverse

これでテストが通るようになる。

$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.045s

OK
Destroying test database for alias 'default'...

次にもう一つテストを追加する。
resolve()はURLからビューに変換する関数でresolve('/')の場合はhomeに変換される。

from django.urls import reverse, resolve
from django.test import TestCase
from .views import home

class HomeTests(TestCase):
    def test_home_view_status_code(self):
        url = reverse('home')
        response = self.client.get(url)
        self.assertEquals(response.status_code, 200)

    def test_home_url_resolves_home_view(self):
        view = resolve('/')
        self.assertEquals(view.func, home)

再びテストを実行してOKになることを確認する。
3行目の部分はテストが成功している場合は..のような表示になり、エラーがあるとE.のような表示(2つあるテストの最初がエラー)になる。

$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.019s

OK
Destroying test database for alias 'default'...


verbosityオプション

テストの詳細を出力したい場合は--verbosityオプション(0〜3)を指定する。
それぞれのレベルの出力は以下の通り。


0=minimal output

$ python manage.py test --verbosity 0
System check identified no issues (0 silenced).
----------------------------------------------------------------------
Ran 2 tests in 0.020s

OK


1=normal output

$ python manage.py test --verbosity 1
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.019s

OK
Destroying test database for alias 'default'...


2=verbose output

クリックで展開

$ python manage.py test --verbosity 2
Creating test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
Operations to perform:
  Synchronize unmigrated apps: django_extensions, messages, staticfiles
  Apply all migrations: admin, auth, boards, contenttypes, sessions
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying boards.0001_initial... OK
  Applying sessions.0001_initial... OK
System check identified no issues (0 silenced).
test_home_url_resolves_home_view (boards.tests.HomeTests) ... ok
test_home_view_status_code (boards.tests.HomeTests) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.021s

OK
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...


3=very verbose output

クリックで展開

$ python manage.py test --verbosity 3
Creating test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
Operations to perform:
  Synchronize unmigrated apps: django_extensions, messages, staticfiles
  Apply all migrations: admin, auth, boards, contenttypes, sessions
Running pre-migrate handlers for application boards
Running pre-migrate handlers for application admin
Running pre-migrate handlers for application auth
Running pre-migrate handlers for application contenttypes
Running pre-migrate handlers for application sessions
Running pre-migrate handlers for application django_extensions
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
Running migrations:
  Applying contenttypes.0001_initial... OK (0.008s)
  Applying auth.0001_initial... OK (0.029s)
  Applying admin.0001_initial... OK (0.024s)
  Applying admin.0002_logentry_remove_auto_add... OK (0.019s)
  Applying contenttypes.0002_remove_content_type_name... OK (0.040s)
  Applying auth.0002_alter_permission_name_max_length... OK (0.010s)
  Applying auth.0003_alter_user_email_max_length... OK (0.029s)
  Applying auth.0004_alter_user_username_opts... OK (0.037s)
  Applying auth.0005_alter_user_last_login_null... OK (0.021s)
  Applying auth.0006_require_contenttypes_0002... OK (0.001s)
  Applying auth.0007_alter_validators_add_error_messages... OK (0.017s)
  Applying auth.0008_alter_user_username_max_length... OK (0.017s)
  Applying auth.0009_alter_user_last_name_max_length... OK (0.016s)
  Applying boards.0001_initial... OK (0.110s)
  Applying sessions.0001_initial... OK (0.005s)
Running post-migrate handlers for application boards
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Running post-migrate handlers for application admin
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Running post-migrate handlers for application auth
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Running post-migrate handlers for application contenttypes
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Running post-migrate handlers for application sessions
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Running post-migrate handlers for application django_extensions
System check identified no issues (0 silenced).
test_home_url_resolves_home_view (boards.tests.HomeTests) ... ok
test_home_view_status_code (boards.tests.HomeTests) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.022s

OK
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...


まとめ

  • テストはpython manage.py testコマンドで実行
  • テストの作成はtest.pyを編集
  • reverse()はビューからURLに変換する関数で、Django 2.0ではfrom django.urls import reverse()
  • resolve()はURLからビューに変換する関数
  • --verbosityオプションで出力レベルを変更可能