2020年8月19日水曜日

pytest でテストのカバレッジを表示する方法とカバレッジを上げるコツ

概要

過去に pytest で monkeypatch を当てるテクニックを紹介しました
今回は pytest でテストのカバレッジを表示する方法を初回します

環境

  • macOS 10.15.5
  • Python 3.8.3
    • pytest 6.0.1
    • pytest-cov 2.10.1

使用するコード

  • vim user.py
from http.client import HTTPSConnection


class User():
    FAVORITE_FRAMEWORKS = {
        "ruby": "sinatra",
        "swift": "spritekit",
        "python": "flask"
    }

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def hello(self):
        return("%s,%i" % (self.name, self.age))

    def access(self):
        con = HTTPSConnection("kaka-request-dumper.herokuapp.com")
        con.request('GET', '/')
        res = con.getresponse()
        return res.read().decode()
  • vim test_user.py
import sys
from user import User

def test_hello(monkeypatch):
    monkeypatch.setattr(User, "hello", lambda self: "hawksnowlog,5")
    u = User("hawksnowlog", 10)
    assert(u.hello() == "hawksnowlog,5")

def test_access(monkeypatch):
    def mock_return(self):
        return '{"key":"value"}'

    monkeypatch.setattr(User, "access", mock_return)
    u = User("hawksnowlog", 10)
    assert(u.access() == '{"key":"value"}')

def test_langs(monkeypatch):
    monkeypatch.setitem(User.FAVORITE_FRAMEWORKS, "ruby", "rails")
    assert(User.FAVORITE_FRAMEWORKS["ruby"] == "rails")

def test_syspath(monkeypatch):
    monkeypatch.syspath_prepend("hoge")
    assert(sys.path[0] == "hoge")

pytest-cov のインストール

pytest-cov という pytest のプラグインがあるのでこれを使います

  • pipenv install pytest-cov

テスト実行

とりあえずコンソールにカバレッジを表示するだけであれば --cov を付与するだけです
値はソースがあるプロジェクトのパスになります

  • pipenv run pytest --cov=.

結果は .coverage という SQLite 形式のファイルが出力されます

テスト結果を HTML で出力する

HTML ファイルで出力する場合には --cov-report=html を使います

  • pipenv run pytest --cov=. --cov-report=html
  • open htmlcov/index.html

htmlcov/index.html というファイルを開くと結果が確認できます
以下のような感じでまだテストしていないところを確認するのに便利です

テスト用のコンフィグファイルを作成する

オプションで指定せず設定ファイルにテストのルールなどをあらかじめ記載しておくこともできます
例えば HTML 結果の出力先を変更したい場合には以下のようにします

  • vim .coveragerc
[html]
directory = coverage_html_report
  • pipenv run pytest --cov=. --cov-report=html

カバレッジを上げるコツ

monkeypatch を使ったテストを書いているとその部分のコードは通らないことになるのでカバレッジは上がりません
monkeypatch を使ってカバレッジを上げるポイントとしては monkeypatch する関数などは pypi からインストールしたライブラリの関数にしましょう

例えばサンプルコードで access 関数のカバレッジを上げたい場合は access 関数をパッチするのではなく HTTPSConnection をパッチします

def test_access(monkeypatch):
    class DummyHTTPResponse:
        def __init__(self):
            pass

        def read(self):
            return b'{"key":"value"}'

    class DummyHTTPSconnection:
        def __init__(self):
            pass

        def request(self, method, path):
            return None

        def getresponse(self):
            return DummyHTTPResponse()

    monkeypatch.setattr(HTTPSConnection, "request", DummyHTTPSconnection.request)
    monkeypatch.setattr(HTTPSConnection, "getresponse", DummyHTTPSconnection.getresponse)
    u = User("hawksnowlog", 10)
    assert(u.access() == '{"key":"value"}')

HTTPSConnection で使用している関数だけにパッチを当てれば OK です
return がオブジェクトの場合などはダミー用のクラスを作成してそのダミー用クラスのオブジェクトを返すようにします
そうすることで http.client 側のライブラリの実体ではなくダミー用の関数がコールされるためテストもちゃんと通ることになりカバレッジも下がらずに済みます

最後に

pytest-cov を使ってカバレッジを表示する方法を紹介しました
monkeypatch と組み合わせて使うことが多いと思いますがその場合はちゃんとパッチを当てる箇所を考えましょう
闇雲にパッチを当ててもカバレッジが上がらないので少しコツと経験感的なものが必要になりそうです

参考サイト

0 件のコメント:

コメントを投稿