目的
今回パスワード忘れた時の再設定機能に使うviewのテストを作りました。
と言ってもDjangoには既にPasswordResetConfirmViewがあり、
ほとんど自前で書くところはなかったので、getとpost時の挙動だけを確認します。
甘く見ていたら意外と詰まるところが多かったです。
テスト対象のview
# views.py
class PasswordResetConfirm(PasswordResetConfirmView):
form_class = MySetPasswordForm
template_name = 'users/password_reset_confirm.html'
success_url = reverse_lazy('users:password_reset_complete')
MySetPasswordFormはDjangoのSetPasswordFormをBootstrapに対応しただけの
form。
テスト
Getのテスト
class PasswordResetConfirm(TestCase):
"""パスワード再入力viewのテスト"""
def setUp(self):
"""ユーザーを登録し、uidとtokenを準備"""
from django.contrib.auth import get_user_model
User = get_user_model()
user = User.objects.create_user(
email='test@test.com',
password='test_password',
)
user.save()
self.uid = urlsafe_base64_encode(force_bytes(user.pk))
self.token = default_token_generator.make_token(user)
def test_get(self):
"""getリクエスト"""
url = reverse(
'users:password_reset_confirm',
kwargs={
'uidb64': self.uid,
'token': self.token,
}
)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response,'users/password_reset_confirm.html')
上記はステータスのAssertionErrorが起きてしまいます。
理由は、PasswordResetConfirmViewの内部でtokenをset-passwordに変えてリダイレクトしてるからです。
修正後のテストコード。
class PasswordResetConfirm(TestCase):
"""パスワード再入力viewのテスト"""
def setUp(self):
"""ユーザーを登録し、uidとtokenを準備"""
from django.contrib.auth import get_user_model
User = get_user_model()
user = User.objects.create_user(
email='test@test.com',
password='test_password',
)
user.save()
self.uid = urlsafe_base64_encode(force_bytes(user.pk))
self.token = default_token_generator.make_token(user)
def test_get(self):
"""getリクエスト"""
url = reverse(
'users:password_reset_confirm',
kwargs={
'uidb64': self.uid,
'token': self.token,
}
)
response = self.client.get(url)
# getリクエスト時、
# token部分をset-passwordに置き換えて
# リダイレクトされることの確認
# ステータス302
self.assertEqual(response.status_code, 302)
# リダイレクトpassword_reset_confirm.html
redirect_url = reverse(
'users:password_reset_confirm',
kwargs={
'uidb64': self.uid,
'token': 'set-password',
}
)
self.assertRedirects(
response,
redirect_url
)
Postのテスト
class PasswordResetConfirm(TestCase):
"""パスワード再入力viewのテスト"""
# setUp()
# test_get()
def test_post(self):
"""postリクエスト"""
data = {
'password1': 'test_password2',
'password2': 'test_password2',
}
post_url = reverse(
'users:password_reset_confirm',
kwargs={
'uidb64': self.uid,
'token': 'set-password',
}
)
response = self.client.post(post_url, data=data)
self.assertEqual(response.status_code, 302)
self.assertRedirects(
response,
reverse('users:password_reset_complete')
)
上記もまたAssertionErrorが起きてしまいます。
理由は最初のget時にトークンをセッションに保存しているためで、
このテストはいきなりpostしてしまっているからです。
# 修正後
class PasswordResetConfirm(TestCase):
"""パスワード再入力viewのテスト"""
# setUp()
# test_get()
def test_post(self):
"""postリクエスト"""
# PasswordResetConfirmViewはget時にトークンをsessionに保存し、
# post時にsessionのトークンをチェックしているため
# 初めにgetリクエストを投げる
get_url = reverse(
'users:password_reset_confirm',
kwargs={
'uidb64': self.uid,
'token': self.token,
}
)
self.client.get(get_url)
# 次にpostを投げる
data = {
'password1': 'test_password2',
'password2': 'test_password2',
}
post_url = reverse(
'users:password_reset_confirm',
kwargs={
'uidb64': self.uid,
'token': 'set-password',
}
)
response = self.client.post(post_url, data=data)
self.assertEqual(response.status_code, 302)
self.assertRedirects(
response,
reverse('users:password_reset_complete')
)
まだAssertionErrorが起きてしまいます。
今回はpostで送るデータが問題で、SetPasswordFormのフィールドは
password1
ではなく、new_password1
を使っています。
修正後のテストコードが以下です。
# 2度目の修正後
class PasswordResetConfirm(TestCase):
"""パスワード再入力viewのテスト"""
# setUp()
# test_get()
def test_post(self):
"""postリクエスト"""
# PasswordResetConfirmViewはget時にトークンをsessionに保存し、
# post時にsessionのトークンをチェックしているため
# 初めにgetリクエストを投げる
get_url = reverse(
'users:password_reset_confirm',
kwargs={
'uidb64': self.uid,
'token': self.token,
}
)
self.client.get(get_url)
# 次にpostを投げる
data = {
'new_password1': 'test_password2',
'new_password2': 'test_password2',
}
post_url = reverse(
'users:password_reset_confirm',
kwargs={
'uidb64': self.uid,
'token': 'set-password',
}
)
response = self.client.post(post_url, data=data)
self.assertEqual(response.status_code, 302)
self.assertRedirects(
response,
reverse('users:password_reset_complete')
)
これでやっと、getとpostが期待通りに動くことの確認ができました。
まとめ
Djangoに頼りすぎて、テストが全然かけていなかったですが、
Djangoの実際のソースコードを見ることが多く、良い経験になりましたし、
すごい重要だということが今回分かりました。
今回のアプリ作成で初めてテストを書き始めたので、
そもそもテストの仕方が良くないところがあると思いますが、
自分と同じようにPasswordResetConfirmViewのテストをしたい方の参考になれば幸いです。