Django Rest Framework(DRF)のテストを書くときのTips

# 目的
Djangoでテストを書くときに、知っているとちょっと便利かもというTipsを集めました。


# 前提
- テストコードの書き方については説明しません。
- この記事内では、Tipsの紹介にとどめ、詳細については記載しません。
- DRFをインストールするだけで使えるものに絞っています。
- unittestに元々備わっている機能と、djangoが拡張した機能を混ぜて紹介しています。違いが分かるように各Tips紹介の頭に目印をつけています。

### バージョン
```
Django==4.1.9
djangorestframework==3.14.0
```

# Tips
## 実行時オプション
### keepdb
`django`

テストを実行するたびにテスト用のDBを作り直すということがなくなるので、実行時間を短縮することができます。
```
python manage.py test --keepdb
```

### tag
`django`

テストケースにタグをつけることで、指定したタグのテストケースのみを実行することができます。
また、特定のタグを実行しないようにすることもできます。
https://docs.djangoproject.com/en/4.2/topics/testing/tools/#tagging-tests

```py:test.py
from django.test import tag

class SampleTestCase(TestCase):
    @tag("fast")
    def test_fast(self):
        ...

    @tag("slow")
    def test_slow(self):
        ...

    @tag("slow", "core")
    def test_slow_but_core(self):
        ...
```

```
python manage.py test --tag=fast --tag=core
python manage.py test --exclude-tag=slow
```

### test_name_patterns
`unittest`

`-k`オプションに文字列を指定すると、その文字列を含むテストクラス、テストケースのみを実行することができます。

例えば、`-k foo`には`foo_tests.SomeTest.test_something`や`bar_tests.SomeTest.test_foo`はマッチしますが、`bar_tests.FooTest.test_something`にはマッチしません。

```
python manage.py test -k foo
```

### settings
`django`

settings.pyを複数用意している場合、テストコマンド実行時だけ切り替えることができます。
```
python manage.py test --settings=config.settings.test
```

## テストコード
### 継承元クラス
`django`

テストクラスの継承元には、DRFだと`rest_framework.APITestCase`を指定することが多いと思います。
しかし、リクエストを送ったり、DB接続を必要としないのであれば`django.test.testcases.SimpleTestCase`で十分です。

実行速度の点でも、APIもDB接続も必要としないことを明示するという点でも、継承元クラスは書き分けた方がいいと思います。

テストクラスの継承関係は次のようになっています。
`rest_framework.APITestCase`は`django.test.testcases.TestCase`を継承しています。
https://docs.djangoproject.com/en/4.2/topics/testing/tools/#provided-test-case-classes
![その他のテストクラス](https://docs.djangoproject.com/en/4.2/_images/django_unittest_classes_hierarchy.svg)


### fixtures
`django`

DBを使用するテストをするとき、データはfixturesを使って入れることができます。
```py:test.py
class SampleTestCase(APITestCase):
    fixtures = ["test.json"]
```


fixtureは`manage.py dumpdata`で作成することができます。
詳しい作成方法については、こちらなどを参考にしてください。
- https://docs.djangoproject.com/en/4.2/ref/django-admin/#dumpdata
- https://marsquai.com/745ca65e-e38b-4a8e-8d59-55421be50f7e/05f253f8-c11b-4c91-8091-989eb2600a7b/229d6ef3-1ba3-4e16-bdd7-c5f01c6dfb12/


### setup
`django`, `unittest`

`unittest.test.TestCase`には以下のメソッドが用意されています。
<dl>
  <dt>setUpClass()</dt>
    <dd>テストクラスの最初に1度だけ呼ばれる。</dd>
  <dt>setUp()</dt>
    <dd>各テストケースの最初に呼ばれる</dd>
  <dt>tearDown()</dt>
    <dd>各テストケースの最後に呼ばれる</dd>
  <dt>tearDownClass()</dt>
    <dd>テストクラスの最後に1度だけ呼ばれる</dd>
</dl>

また、`django.test.testcases.TestCase`では次のメソッドが追加されています。
<dl>
  <dt>setUpTestData()</dt>
    <dd>setUpClassの最後に呼ばれる</dd>
</dl>

これらのメソッドをオーバーライドすることで、共通な前処理や後処理を簡潔に書くことができます。

```py:test.py
class SampleTestCase(APITestCase):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        ...

    @classmethod
    def setUpTestData(cls):
        ...

    def setUp(self):
        ...
```
TestCaseではsetUpClass()を次のようにオーバーライドしています。
**トランザクションの開始、fixturesの投入、setUpTestData、をこの順で実行する。**
そのため、setUpClass()をオーバーライドするときは、最初にsuper()で親のsetUpClass()を呼ぶようにします。

### メッセージ
`unittest`

各テストケースにdocstringを書いておくと、実行時オプションで`-v`を2以上にしたときに、その内容が出力されます。

```py:test.py
class SampleTestCase(APITestCase):
    def test_case(self):
        """サンプルテストケースです"""
        ...

---
test_case (project.app.test.SampleTestCase)
サンプルテストケースです ... ok
```

# おわりに
ほかにもこんなTipsあるよ！といったものは是非コメントで教えていただけるとありがたいです。