Django | assertQuerysetEquaでFAILEDエラーになってしまう件の備忘録 | Python

Django01

皆さんこんにちは。文系大学出身の転職系エンジニアの自分は、機械学習の複雑な数学を学習するために膨大な時間が必要だとひしひしと感じている小幡です。

本日はDjangoのチュートリアル作業中に発生したエラーについてです。

公式ドキュメントにある「はじめての Django アプリ作成、その 5」の中段過ぎ「新しいビューをテストする」の項目で提示されているコードを例としてテストを実行すると、私の環境ではエラーが発生します。

今回はこのエラーの解消方法をご紹介します(環境依存のエラーかもしれませんので、参考される際はご注意ください。)

テストはプログラミングに置いて欠かせない物なので、しっかりエラー解消していきます。コーディング1割、テスト9割の重みを自分の中に課しています。

エラー内容の紹介

こちらに、同じエラーになって困っているという投稿が2013年にあったようなので、リンクを貼っておきます。まさに私もこのような状況に陥ってしまいました。詳しいエラー内容はリンク先を参照願います。

簡単に説明すると、”self.assertQuerysetEqual”で比較しているものが、シングルクォーテーション付きのものと、無しのものになってしまい、エラーになっているという状況です。(以下の画像はテスト結果ですが、チュートリアルはtests.pyに全てのテストを記述していますが、モジュール事にテストを切り分けている点が少し違います。またファイルのパスはdjango_website/Rankyという形になっています。)

公式ドキュメントという事と、Twitterでエラーについて聞いてみたり、他に同じエラーで悩んでいる人がググっても見受けられなかったので、環境依存の特殊ケースなのかもしれません。

テスト環境
Python:3.9.2
Django:3.0.2

行ったエラー解決方法

結果的にはdspacejs氏のコメントの指示通り修正したところ解決しました。

dspacejs氏の投稿

具体的なコードは以下になります。

11行目が追加したコードです。3つ目の引数transformにlambdaを渡しています。

    def test_past_question(self):
        """
        Questions with a pub_date in the past are displayed on the
        index page.
        """
        question = create_question(question_text="Past question.", days=-30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            [question],
            transform=lambda x: x,
        )

lambdaについては、こちら(Pythonのlambda(ラムダ式、無名関数)の使い方)を参照されると良いかと思います。

簡単に説明すると、lambdaは名前のない関数を定義します。

func = lambda x: x

上の例で説明すると、funcという名前の変数に、lambdaで作った関数を入れています。そのlambda関数ですが最初のxが引数、コロン後のxが式という事になります。つまり、受け取った引数をそのまま返却する関数をlambdaで作成し、funcに入れているということです。

repr()とは?

今回lambdaで引数を渡しましたが、デフォルトではrepr()が渡されています。このrepr()とは何なのか?その疑問はこちら(Pythonのstr( )とrepr( )の使い分け)を参照されると解決できると思います。

簡単に説明すると、str()と似ているけれど、repr()の方がデバッグ用として引数用のコンストラクタを返却しているので、単純な文字列と比較してしまうと異なっていると判定されてしまうということです。

結果としてrepr()の代わりにlambda(そのまま)を渡す

assertQuerysetEqualのtransform引数は、repr関数をデフォルトの引数として受け取っている(関数を引数としている)ので、修正するためには引数に関数を渡してあげる必要があるというイメージで、無名関数を渡して解決しました。という感じです。assertQuerysetEqualの中身を見てみると少し違うので、興味がある方は実際の関数を参照ください。

他の解決方法を模索した話

上記の方法でなんとか結果が’OK’と表示されたわけで、テストはクリアされたわけですが、この解決方法にたどり着くまでには様々な方法を試しましたが、どれもだめでした。

1、VSCodeのデバッグで変数を取得しようと頑張る

この方法は変数の中身を見る事で、解決方法がわかるのではないか?という思惑からスタートしています。Djangoを使わないPythonのコードでは、このVSCodeのデバッグ機能が非常に便利で、よく使っているのですが、Djangoのtestはmanage.pyから起動しているためか、上手くデバッグを実行できませんでした。

実行できたケースでもコマンドラインからチュートリアルにあるコマンドをVSCodeのデバッグボタンで省略実行できるようになったレベルで、ステップインなどの機能、ブレイクポイントを付けて変数の中身を確認するということができませんでした。

具体的にはsetting.jsonに以下を記述しました。

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Django",
            "type": "python",
            "request": "launch",
            "program": "${workspaceFolder}\\ranky\\manage.py",
            "args": [
                "test",
                "--keepdb",
                "polls",
            ],
            "django": true,
            "python": "${workspaceRoot}/Scripts/python.exe"
        }
    ]
}

このsetting.jsonのポイントは11行目”program”と12行目”args”です。

11行目では”manage.py”のパスを設定しています。

12行目では”manage.py”以降に記述するコマンドを引数としています。結果的に以下のコマンドを実行しているイメージです。

python .\manage.py test --keepdb polls

以上によってVSCodeのデバッグ開始ボタンによって、コマンドラインにコマンドを打ち込む手間が減る恩恵を受けることができますが、あまり意味はありません。

2、ひたすらprint関数で変数の中身を確認する

これはもっともやりたくない手段ですが、ひたすら変数の中身を確認しながら、なんとかシングルクォーテーションを削除できないか?と模索していました。

Python標準の文字列から指定文字列を削除する方法などを考えましたが、それは果たしてテストとして正しい結果を返していると言えるのか?という疑問と、結果的に返却される値がリストの中に入っているので、リスト内包表記をしようするのか?という沼にハマりかけたので一旦中止しました。

このようにして1週間くらいエラーに振り回されていましたが、1番最初にあげた記事にたどり着き、lambda関数を使用し、テストを無事クリアすることができました。

まとめ

私のケースではassertQuerysetEquaでエラーになった場合、transform引数にlambdaを渡すことで解決できました。

エラーになった根本原因は使用しているバージョンのせいなのか、何なのかまだはっきりとわかっていません。

先ほども書きましたが、公式ドキュメントのテストを実行していますので、環境依存のエラーの可能性が高いと考えて居ますが、同じエラーで困っている方の助けになれば幸いです。

それではまた、みなさんにお会いできることを楽しみにしています。

未経験から転職エンジニアを目指す人を応援したいコミュニティも運営していますので、ご興味あればのぞいて見てください。コミュニティはコチラ

この記事を書いた人

小幡 知弘

1990年茨城県神栖市生まれ
2013年大阪芸術大学卒業
Python×Webエンジニア