LoginSignup
0

posted at

updated at

Djangoのfixtureで登録したユーザーでテストClientのログイン認証に一瞬だけ手間取った話

開発環境

  • Python 3.9
  • Django==3.2.12
  • djangorestframework==3.13.1

困ったこと。

DjangoでModelSetViewを使ったAPIの開発で、
ログイン認証の必要なAPIのテストをしているとき。

tests.py
class TestAuthApi(TestCase):
    '''認証APIのテスト'''

    AUTH_URL = '/api-token-auth/' # 認証用URL
    fixtures = ['fixtures/test/test_data.json']
    
    def test_token(self):
        '''POSTメソッドのテスト'''
        client = Client()
        # トークン取得
        token_response = client.post(
            self.AUTH_URL,
            data={
                "username": "testuser",
                "password": "password",
                "email": "testuser@test.com",
            },
            content_type='application/json',
        )
        # トークン認証のチェック
        self.assertEqual(token_response.status_code, 200)
        self.assertTrue('token' in token_response.json())

python manage.py testで実行すると、

AssertionError: 401 != 200

がraiseされた。
認証できていないということですね。

ちゃんと、ユーザー名も、パスワードも、メールアドレスも間違っていないことを確認しているので、なにが間違っているのだろう?
(python manage.py createsuperuserで作ったユーザーでは、Webブラウザ、API共に認証できているので、User周りの設定が間違っていることはないだろう。)

関連するファイルの関連する箇所

models.py
from django.contrib.auth.models import AbstractUser
()

class User(AbstractUser):
    pass

()
serializers.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
()

class UserSerializer(serializers.ModelSerializer):
    """ユーザーシリアライザー"""
    class Meta:
        model = get_user_model()
        fields = ('id', 'username', 'email')
()
urls.py
from rest_framework.authtoken import views as auth_views
()

urlpatterns = [
    ()
    # トークン認証用エンドポイント
    url(r'^api-token-auth/', auth_views.obtain_auth_token),
]
settings.py
()

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework.authtoken',
    ()
]

()

AUTH_USER_MODEL = 'apiv1.User'

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES':[
        'rest_framework.authentication.TokenAuthentication', # トークン認証
        'rest_framework.authentication.SessionAuthentication', # セッション認証
    ]
}

()
test_data.json
[
    {
        "model": "アプリ名.user",
        "pk": 1,
        "fields": {
            "username": "testuser",
            "password": "password",
            "email": "testuser@test.com",
            (略)
        }
    },
]

解決策

「Webブラウザ、APIでは認証できているのに、テストClientでは認証できない。」
→ということは、テスト用データが間違っているのでは?

よくよく考えたら、テスト用データにパスワードの平文を書いたけど、DBに平文が保存されるはずないよね、、、

ということで調べると、Djangoではmake_password関数を使って、パスワードをハッシュ化して保存している。

なので、fixtureのデータをパスワードのハッシュ値にすればよい。
ということで、ハッシュ値の取得を行う。

# python manage.py shell
>>> from django.contrib.auth.hashers import make_password
>>> make_password('password')
'pbkdf2_sha256$260000$O2CJugvRp7L8GMfcLfv8eZ$X8Mf8PPmtf37Emg2nD42bJ/m3PQF+jYVpgKEifiDbHo='

これを、fixtureに入れて、

test_data.json
[
    {
        "model": "アプリ名.user",
        "pk": 1,
        "fields": {
            "username": "testuser",
            "password": "pbkdf2_sha256$260000$O2CJugvRp7L8GMfcLfv8eZ$X8Mf8PPmtf37Emg2nD42bJ/m3PQF+jYVpgKEifiDbHo=",
            "email": "testuser@test.com",
            (略)
        }
    },
]

として、再度python manage.py testを実行すると、

OK!!!

初歩的なミスでしたが、こういうのを地道に解決していく過程でフレームワークへの理解が深まったりしますよね。
(「テスト用のユーザー登録からやればいいじゃん」というよのは内緒。)

参考

https://medium.com/p/64a4b38904ff#9c63
(ちょっと古いけど。)

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0