個人的備忘録。
やること
- Github Actionsで
pytest
を動かして単体テストする。- 動作タイミングはpull requestのopen時と、pull requestがopenされたブランチへのpush時
- 外部APIを叩くテストケースはスキップすることにした
- 理想的にはAPIを叩くロジックを分離してDependency Injectionすることで、テスト時にはモックAPIが動作するようにするべきとされています
- 参考: PythonでDependency Injection (DI)をやるには?
-
pytest-cov
でカバレッジを出して、pull requestのコメントとして投稿する。
Github Actionsの設定
name: Pytest
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: 'pip'
- name: Install dependencies
run: pip install -r devel-requirements.txt
- name: run pytest
run: |
set -o pipefail
python -m pytest --junitxml=pytest.xml --cov-report=term-missing --cov=src tests/ | tee pytest-coverage.txt
- name: Pytest coverage comment
uses: MishaKav/pytest-coverage-comment@v1.1.47
with:
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml
以下に補足していきます。
pull requestのopen時と更新時に動作させる
on:
pull_request:
types: [opened, reopened, synchronize]
pull requestがopenされているブランチへのpushイベントで動かしたい時は、synchronize
を指定します。
カバレッジを取得する
- name: run pytest
run: |
set -o pipefail
python -m pytest --junitxml=pytest.xml --cov-report=term-missing --cov=src tests/ | tee pytest-coverage.txt
pytest-coverage-commentの公式例とほぼ同じですが、パイプを使う場合、pipefail
の設定をしないとパイプ後半の戻り値で成功・失敗を判断することになるので、単体テストが失敗していてもCIが通ってしまいます。
そこで、set -o pipefail
を設定しています。
pull requestのコメントとして投稿する
- name: Pytest coverage comment
uses: MishaKav/pytest-coverage-comment@v1.1.47
with:
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml
--cov-report
にterm-missing
を指定するとテストされていない行を表示してくれます。
GitHub Actionsでの実行時にはskipするテストケースを設定する
GITHUB_ACTION
環境変数が設定されるのを利用して、GitHub Actionsでの実行時にskipします。
@pytest.mark.skipif('GITHUB_ACTION' in os.environ, reason="skip external api call during CI")
def test_foobar():
# GitHub Actionsではskipされるテストケース
pass
参考:
ハマりどころ
src
のサブディレクトリにある.py
ファイルに関して、テストはされるのですがcoverage reportに入らないというトラブルがありました。
調べてみると、pytest-cov
はサブディレクトリに__init__.py
がないとそこで探索をやめてしまうという動作になっているようでした。
そこで、src
のサブディレクトリにそれぞれ空の__init__.py
を作成したところ、うまく動作しました。
TODO
READMEにカバレッジのバッジをつけたい。