5
2

More than 1 year has passed since last update.

pre-commitでテスト自動化

Posted at

こんにちは、えいりんぐーです

みなさんはテストを書いていますか?
(ここでは単体テストを指しています)
また、テストを自動化していますか?

Circle CI や Travis CI などで自動テストを実施している方も多いかと思います
一方で、制限の厳しい状況 (クライアントや社内事情など) で仕事をしていると、容易には外部サービスを利用できない場合がありますね

今回は pre-commit を使って git push 時にテストを自動で走らせる方法をご紹介します

pre-commit

pre-commit は Python 製の Git hook のラッパーです

自前でいちいちスクリプトを書くことなく、よくある処理 (リンタやフォーマッタなど) を実装することができます

フック自体には、コミット時やマージ時などの所定のタイミングをトリガーとすることができます

pre-push フックを使えば、プッシュ時にテストが自動で走るように設定できます

余談

pre-commit にはすでに多くのフックがサポートされていますが、pytest のフックは実装されていないようです

要望などは何度か上がっているようですが、コミット時に走らせるには、テストが遅いことがネックなようです
実際、コミットするたびにテストは過剰ですよね

pre-push の設定

.pre-commit.yaml
repos:
-   repo: local
    hooks:
    -   id: test
        name: test
        entry: pytest -v -s tests
        stages:
            - "push"
        language: system
        pass_filenames: false
        always_run: true

stages"push" を指定すると、プッシュ時にフックを実行します

entry に実行するコマンドを設定します

pre-commit install -t pre-push で pre-push フックをインストールします

コード例

ディレクトリ構成

+ .pre-commit-config.yaml
+ mypackage
|  + __init__.py
|  + foo.py
|
+ tests
|  + __init__.py
|  + test_foo.py
|
+ requirements.txt
foo.py
def foo():
    return "bar"
test_foo.py
from mypackage import foo


def test_foo():
    assert foo.foo() == "foo"

このコードをプッシュしようとするとテストが走り、失敗するため、プッシュにも失敗します

$ git push origin pre-commit
test.....................................................................Failed
- hook id: test
- exit code: 1

============================= test session starts =============================
platform win32 -- Python 3.10.6, pytest-7.1.3, pluggy-1.0.0 -- C:\Users\***\Programs\mypackage\.venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\***\Programs\mypackage
collecting ... collected 1 item

tests/test_foo.py::test_foo FAILED

================================== FAILURES ===================================
__________________________________ test_foo ___________________________________

    def test_foo():
>       assert foo.foo() == "foo"
E       AssertionError: assert 'bar' == 'foo'
E         - foo
E         + bar

tests\test_foo.py:5: AssertionError
=========================== short test summary info ===========================
FAILED tests/test_foo.py::test_foo - AssertionError: assert 'bar' == 'foo'
============================== 1 failed in 0.09s ==============================

foo 関数の返り値をテストが通るように修正してプッシュします

テストを通過するのでプッシュが成功します

$  git push origin pre-commit
test.....................................................................PassedEnumerating objects: 26, done.
Counting objects: 100% (26/26), done.
Delta compression using up to 8 threads
Compressing objects: 100% (22/22), done.
Writing objects: 100% (25/25), 2.12 KiB | 1.06 MiB/s, done.
Total 25 (delta 9), reused 2 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (9/9), done.
remote:
remote: Create a pull request for 'pre-commit' on GitHub by visiting:
remote:      https://github.com/***/mypackage/pull/new/pre-commit
remote:
To github.com:***/mypackage
 * [new branch]      pre-commit -> pre-commit

テストが成功するまでプッシュできないので、テストを回避しない限りは、ある意味テスト駆動開発が強制されることになります笑

終わり

如何だったでしょうか

テストを実行するコマンドをいちいち実行しなくても、プッシュするだけでテストが走るので便利ですね
また、Circle CIなどでテストを実行する場合と違い、ローカルで実行するため、レスポンスを早く得られるというメリットもあります

pre-commit には他のタイミングや処理のフックもあるので、ぜひご活用ください
本記事がみなさんの参考になれば幸いです

5
2
0

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
  3. You can use dark theme
What you can do with signing up
5
2