pytest mark を使用した実行するテストの絞り込み

この記事は[ケーシーエスキャロット Advent Calendar 2021](https://qiita.com/advent-calendar/2021/kcscarrot)の19日目の記事です。

皆さん、`python` で単体テストを行う際、`pytest` を使用する場面が多いのではと思います。

例えば、以下のような構成でファイルがあったとします。

```bash
.
└──calc
   ├── calc.py
   └── test_calc.py
```

```python(calc.py)
#!/usr/bin/env python

def add(x: int, y: int) -> int:
    return x + y


def sub(x: int, y: int) -> int:
    return x - y


def adds(x: int, y: int, z: int) -> int:
    return add(add(x, y), z)
```

```python(test_calc.py)
#!/usr/bin/env python

import calc


def test_add():
    assert calc.add(6, 3) == 9


def test_sub():
    assert calc.sub(6, 3) == 3


def test_adds():
    assert calc.adds(6, 3, 1) == 10
```

上記の状態で以下のように実行できます。

```bash
$ pytest
========================== test session starts ==========================
platform linux -- Python 3.8.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/username/calc
collected 3 items

test_calc.py ......                                                [100%]
=========================== 3 passed in 0.04s ===========================
```

このように簡単にテストが実行出来て便利ですが、時と場合によっては全部のテストを行いたくないなぁ…、ということもあると思います。

そんな時には `mark` を使用すると便利です！

例えば、`test_calc.py` の中で加算に関するテストのみを行いたい場合、新たに `pytest.ini` ファイルを作成した上で、テストコードを以下のように変更します。

```bash
.
├──calc
│  ├── calc.py
│  └── test_calc.py
└── pytest.ini
```

```ini(pytest.ini)
[pytest]
markers =
  addonly: addition test only
```

```python(test_calc.py)
#!/usr/bin/env python

import calc
import pytest


@pytest.mark.addonly()
def test_add():
    assert calc.add(6, 3) == 9


def test_sub():
    assert calc.sub(6, 3) == 3


@pytest.mark.addonly()
def test_adds():
    assert calc.adds(6, 3, 1) == 10
```

上記の状態で以下のように実行すると、加算に関するテストのみが行えます。

```bash
$ pytest -m "addonly"
========================== test session starts ==========================
platform linux -- Python 3.8.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/username/temp, configfile: pytest.ini
collected 3 items / 1 deselected / 2 selected

test_calc.py ......                                                [100%]
==================== 2 passed, 1 deselected in 0.03s ====================
```

ただし、`pytest.ini` の配置先には注意して下さい。

```bash
.
└──calc
   ├── calc.py
   ├── pytest.ini
   └── test_calc.py
```

このように `pytest` を実行する階層に `pytest.ini` を配置しないと、以下のように `warning` が発生します。

```bash
$ pytest -m "addonly"
========================== test session starts ==========================
platform linux -- Python 3.8.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/username/temp
collected 3 items / 1 deselected / 2 selected

test_calc.py ......                                                [100%]
============================ warnings summary ===========================
calc/test_calc.py:7
  /home/kishinami/temp/calc/test_calc.py:7: PytestUnknownMarkWarning: Unknown pytest.mark.addonly - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
    @pytest.mark.addonly()

calc/test_calc.py:16
  /home/kishinami/temp/calc/test_calc.py:16: PytestUnknownMarkWarning: Unknown pytest.mark.addonly - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
    @pytest.mark.addonly()

-- Docs: https://docs.pytest.org/en/stable/warnings.html
============= 2 passed, 1 deselected, 2 warnings in 0.04s ==============
```

また、逆に加算に関するテスト以外を行いたい場合は `not` を使用すると実現できます。

```bash
$ pytest -m "not addonly"
========================== test session starts ==========================
platform linux -- Python 3.8.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/username/temp, configfile: pytest.ini
collected 3 items / 2 deselected / 1 selected

test_calc.py ......                                                [100%]
==================== 1 passed, 2 deselected in 0.03s ====================
```

なお、`mark` を数種類使用する場合は、`makefile` にコマンドとして記載しておくと便利だと思います。

```makefile
.PHONY: test
test:
	pytest

.PHONY: test-add
test-add:
	pytest -m "addonly"
```

```bash
$ make test-add
========================== test session starts ==========================
platform linux -- Python 3.8.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/username/temp, configfile: pytest.ini
collected 3 items / 1 deselected / 2 selected

test_calc.py ......                                                [100%]
==================== 2 passed, 1 deselected in 0.03s ====================
```

いかがでしたでしょうか？、`mark` を使用することによって、このように絞り込みが行えるので、用途別にテストを実行したい場合に有用かと思います。