django-nose
と django-caverage
を使ってテストコードの実行とカバレッジの計測を行っていましたが、 django 1.10
ではうまくカバレッジを計測してくれないという問題があったので、今回は Coverage.py
を利用してカバレッジを計測してみたいと思います。
django-nose
と django-caverage
を利用したテストコードの実行方法を知りたい方は、過去の記事を参照してください。
django-noseとdjango-caverageを利用していた時の問題点
Django 1.10
でカバレッジを計測していた際に、下記のコードのカバレッジが計測できない問題がありました。
- デコレータのカバレッジが計測されない
- 多重継承しているスーパークラスのカバレッジが計測されない
デコレータのカバレッジが計測されないために、全てのコードをパスしているテストコードを書いてもカバレッジが100%にならず、計測結果をみてもこれ以上テストが必要かどうかをカバレッジから確認することができない状態でした。
そこで、 Coverage.py
を利用してカバレッジを計測するようにしました。
Coverage.py をインストールする
pip
を使って Coverage.py
をインストールします。
$ pip install coverage
Coverage.py の設定ファイルを作成する
プロジェクトディレクトリ直下に Coverage.py
に関する設定を記述する「 .coveragerc
」というファイルを作成します。
とりあえず、最低限の設定を記述しておきます。
[run]
omit = */tests/*
directory = cover
.coveragerc
の設定に関する詳細な情報については下記のサイトを参照してください。
テストコードを準備する
今回は「 accounts
」アプリケーションの View
と Model
をテストを実施します。
下記のテストを実施するテストコードになります。
- Viewのテスト
- ログイン画面へのアクセステスト
- ログイン成功時のテスト
- ログイン失敗時のテスト
- Modelのテスト
- ユーザ作成(create_user)のテスト
- スーパーユーザ作成(create_superuser)のテスト
# accounts/tests.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.utils.encoding import python_2_unicode_compatible
from django.test import TestCase
from .models import AuthUser
@python_2_unicode_compatible
class AccountsViewTests(TestCase):
def setUp(self):
"""
ログイン可能なユーザを1名追加する
"""
AuthUser.objects.create_user(username='test',
email='test@test.com',
password='test',
last_name='テスト',
first_name='太郎')
def test_login_get(self):
"""
ログイン画面へのアクセスのテスト
"""
client = self.client
response = client.get('/accounts/login/')
self.assertEqual(response.status_code, 200)
client.login(username='test', password='test')
response = client.get('/accounts/login/')
self.assertEqual(response.status_code, 200)
def test_login_success(self):
"""
ログイン画面でログインできるかテストする
"""
client = self.client
response = client.post('/accounts/login/', data={'username': 'test', 'password': 'test'})
self.assertRedirects(response, '/')
def test_login_fail(self):
"""
ユーザ登録されている人以外はログインできないことをテストする
"""
client = self.client
response = client.post('/accounts/login/', data={'username': 'test2', 'password': 'test'}, follow=True)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.request['PATH_INFO'], '/accounts/login/')
@python_2_unicode_compatible
class AccountsModelTests(TestCase):
def test_create_user(self):
"""
AuthUserモデルのcreate_userをテストする
"""
# 普通にユーザ追加
result = AuthUser.objects.create_user(username='test', email='test@test.com', password='test', last_name='テスト', first_name='太郎')
user = AuthUser.objects.get(pk=result.pk)
# ちゃんとパラメータにセットした値でユーザが登録されたか確認する
self.assertEqual(result.username, 'test')
self.assertEqual(result.email, 'test@test.com')
# ハッシュ値は常にランダムになってしまうので、ここはDBに登録された値と比較する
self.assertEqual(result.password, user.password)
self.assertEqual(result.last_name, 'テスト')
self.assertEqual(result.first_name, '太郎')
# 以下の3つのフィールドはcreate_userした時に自動でセットされる項目なので、DBのレコードの値を検証する
self.assertTrue(user.is_active)
self.assertFalse(user.is_staff)
self.assertFalse(user.is_superuser)
# eメールが空だったらエラーをはく
# エラー時のメッセージも検証する
with self.assertRaisesMessage(ValueError, 'Users must have an email'):
AuthUser.objects.create_user(username='test', email='', password='test', last_name='テスト', first_name='太郎')
# usernameが空だったらエラーをはく
# エラー時のメッセージも検証する
with self.assertRaisesMessage(ValueError, 'Users must have an username'):
AuthUser.objects.create_user(username='', email='test@test.com', password='test', last_name='テスト', first_name='太郎')
def test_create_superuser(self):
"""
AuthUserモデルのcreate_superuserをテストする
"""
# スーパーユーザを追加する
result = AuthUser.objects.create_superuser(username='super', email='super@user.com', password='user', last_name='スーパー', first_name='ユーザ')
user = AuthUser.objects.get(pk=result.pk)
# ちゃんとパラメータにセットした値でユーザが登録されたか確認する
self.assertEqual(result.username, 'super')
self.assertEqual(result.email, 'super@user.com')
# ハッシュ値は常にランダムになってしまうので、ここはDBに登録された値と比較する
self.assertEqual(result.password, user.password)
self.assertEqual(result.last_name, 'スーパー')
self.assertEqual(result.first_name, 'ユーザ')
# 以下の3つのフィールドはcreate_userした時に自動でセットされる項目なので、DBのレコードの値を検証する
self.assertTrue(user.is_active)
self.assertTrue(user.is_staff)
self.assertTrue(user.is_superuser)
# eメールが空だったらエラーをはく
# エラー時のメッセージも検証する
with self.assertRaisesMessage(ValueError, 'Users must have an email'):
AuthUser.objects.create_superuser(username='super', email='', password='user', last_name='スーパー', first_name='ユーザ')
# usernameが空だったらエラーをはく
# エラー時のメッセージも検証する
with self.assertRaisesMessage(ValueError, 'Users must have an username'):
AuthUser.objects.create_superuser(username='', email='super@user.com', password='user', last_name='スーパー', first_name='ユーザ')
テストコードを実行してカバレッジを HTML で出力する
下記コマンドを実行して、テストコードを実行しカバレッジを HTML ファイルで出力します。
$ coverage run --source=accounts manage.py test accounts
$ coverage report
$ coverage html
上記実行すると .coveragerc
の
で設定した cover
ディレクトリにカバレッジの計測結果が格納されます。
計測されたカバレッジを確認する
cover
ディレクトリの index.html
をブラウザで開くとカバレッジの計測結果が確認できます。
accounts/models.py
のカバレッジの詳細を確認してみます。
デコレータやスーパークラスのコードもカバレッジが通っていることが確認できました!!
最後に
django-nose
もカバレッジを簡単に計測することができて、使い方も簡単なのですが、「 Django 1.8
」以降を利用して開発している場合、 django-nose
が Django
の新機能に対応しておらず、カバレッジが正確に測ることができなくなってしまっていました。
もし、 Django 1.8
以降のバージョンを利用して開発を行っている方で、カバレッジをうまく計測できずにお悩みの方は Coverage.py
に切り替えるのもアリだと思います。
コメント