概要
coverageモジュールのCLIを使ってテストファイルを実行していきます。
実行されたテストファイルのデータに基づいて、テストカバー率のレポートを表示します。
テストのカバー率をHTMLファイルとして作成し分析します。
実行環境 |
---|
Windows Subsystem for Linux |
Python 3.10.0 |
使用ライブラリ | ライセンス |
---|---|
coverage==7.2.3 | Apache-2.0 |
coverageモジュールを使うことで、開発しているモジュールがどれほどテストできているか、どこの範囲をテストするべきかを測定することができます。
測定されたデータに基づいて、コンソール上にレポートとして表示することやHTMLファイルにしてブラウザ上で進捗を確認することができます。
以下の図は、HTMLページとしてカバレッジデータから作成された進捗データです。
「Module」の列は測定されたファイル名で「どのソースコードのテストが行われたか」、または「どこのソースコードのテストを行うべきか」を色分けされた状態で確認することができます。
以下は「greeting.py」のソースコードを確認した図です。
ソースコードの量が多い時などはこのように色分けされて可視化されるので大変ありがたいモジュールです。
テストファイルの作成については以下の記事で簡単に説明しています。
pipを使ってインストールします。
$ pip install coverage
インストールが完了したらcoverageモジュールの「help」コマンドを実行して使用できるコマンドの詳細情報を表示してみます。
$ coverage help
Coverage.py, version 7.2.3 with C extension
Measure, collect, and report on code coverage in Python programs.
usage: coverage <command> [options] [args]
Commands:
annotate Annotate source files with execution information.
combine Combine a number of data files.
debug Display information about the internals of coverage.py
erase Erase previously collected coverage data.
help Get help on using coverage.py.
html Create an HTML report.
json Create a JSON report of coverage results.
lcov Create an LCOV report of coverage results.
report Report coverage stats on modules.
run Run a Python program and measure code execution.
xml Create an XML report of coverage results.
Use "coverage help <command>" for detailed help on any command.
Full documentation is at https://coverage.readthedocs.io/en/7.2.3
各コマンドの詳細情報については「coverage help」コマンドの後に特定のコマンドを指定して実行します。
$ coverage help run
Usage: coverage run [options] <pyfile> [program options]
Run a Python program, measuring code execution.
Options:
-a, --append Append coverage data to .coverage, otherwise it starts
clean each time.
--branch Measure branch coverage in addition to statement
coverage.
--concurrency=LIBS Properly measure code using a concurrency library.
Valid values are: eventlet, gevent, greenlet,
multiprocessing, thread, or a comma-list of them.
--context=LABEL The context label to record for this coverage run.
--data-file=OUTFILE Write the recorded coverage data to this file.
Defaults to '.coverage'. [env: COVERAGE_FILE]
--include=PAT1,PAT2,...
Include only files whose paths match one of these
patterns. Accepts shell-style wildcards, which must be
quoted.
-m, --module <pyfile> is an importable Python module, not a script
path, to be run as 'python -m' would run it.
--omit=PAT1,PAT2,... Omit files whose paths match one of these patterns.
Accepts shell-style wildcards, which must be quoted.
-L, --pylib Measure coverage even inside the Python installed
library, which isn't done by default.
-p, --parallel-mode Append the machine name, process id and random number
to the data file name to simplify collecting data from
many processes.
--source=SRC1,SRC2,...
A list of directories or importable names of code to
measure.
--timid Use a simpler but slower trace method. Try this if you
get seemingly impossible results!
--debug=OPTS Debug options, separated by commas. [env:
COVERAGE_DEBUG]
-h, --help Get help on this command.
--rcfile=RCFILE Specify configuration file. By default '.coveragerc',
'setup.cfg', 'tox.ini', and 'pyproject.toml' are
tried. [env: COVERAGE_RCFILE]
Full documentation is at https://coverage.readthedocs.io/en/7.2.3
ここで言うカバレッジデータとは「coverage」モジュールがテストを実行してその際に収集されたデータのことです。
収集されたデータはデフォルトだと「.coverage」として作成されます。
以下のようなディレクトリの構造上でテストを実行してみます。
.
├── .venv
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
テストを実行させるには、「coverage run」コマンドのオプション「-m(--module)」を指定し、テストを実行するフレームワークを設定します。
以下は各種テストフレームワークの実行例です。
「unittest」モジュールの実装に関してはこちらの記事で説明しています。
# unittestモジュールの例
$ coverage run -m unittest
# pytestモジュールの例
$ coverage run -m pytest
# Djangoプロジェクトの例
$ coverage run -m manage.py test
各自の例で実行しテストが成功すると、カレントディレクトリに「.coverage」が作成されているのが分かります。
.
├── .coverage # new
├── .venv
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
デフォルトでは「.coverage」として作成されますが、「coverage run」コマンドのオプション「--data-file=」もしくは「-p (--parallel-mode)」でカバレッジデータのファイル名を設定することができます。
「--data-file=」オプションでファイル名を設定すると任意のファイル名を決められます。
$ coverage run --data-file=.coverage.greeting -m unittest
ディレクトリ内を確認してみると「.coverage.greeting」ファイルが作成されているのが分かります。
.
├── .coverage
├── .coverage.greeting # new
├── .venv
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
「-p (--parallel-mode)」オプションを付けると、「.coverage」にドット繋ぎで実行しているマシン名に続きプロセスIDと乱数が付与されたファイル名が出来上がります。
$ coverage run -p -m unittest
.
├── .coverage
├── .coverage.greeting
├── .coverage.ideapad.355.490043 # new
├── .venv
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
上記の隠しファイルであるカバレッジデータの中身はどれも一緒です。
データが同じなのであまり意味はありませんが、「coverage combine」コマンドを実行するとカバレッジデータ同士を組み合わせることができます。
$ coverage combine
Combined data file .coverage.greeting
Skipping duplicate data .coverage.ideapad.355.490043
カバレッジデータのベースファイルとして「.coverage」に組み合わさりました。
.
├── .coverage
├── .venv
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
「coverage combine」コマンドは、異なるバージョンやOSで実行され収集されたカバレッジデータを結合することができます。
カバレッジデータのファイル名先頭が「.coverage...」となっている場合は自動で組み合わされますが、そうではない場合は「-a(--append)」オプションでカバレッジデータのファイル名を指定することができます。
$ coverage combine -a cov.data.1 cov.data.2
「coverage run」によって収集されたカバレッジデータを表示するには、「coverage report」もしくは、「coverage html」のコマンドを実行します。
コマンドラインのコンソール上にカバレッジデータのレポートを表示します。
以下のディレクトリ構造上で「coverage run」によりテストランのデータを収集したとします。
.
├── .venv
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
$ coverage run -m unittest
するとカレントディレクトリには「.coverage」というカバレッジデータが作成されます。
.
├── .coverage # new
├── .venv
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
コンソール上でカバレッジデータのレポートを表示するには「coverage report」コマンドを実行します。
$ coverage report
Name Stmts Miss Cover
--------------------------------------------
goodbye.py 9 3 67%
greeting.py 22 12 45%
tests/__init__.py 0 0 100%
tests/test_goodbye.py 7 0 100%
tests/test_greeting.py 7 0 100%
--------------------------------------------
TOTAL 45 15 67%
デフォルトでは実行環境下にある「.coverage」を読み込んで表示するそうです。
オプションの「-m(--show-missing)」を付けると、ソースコードのどの範囲がカバーされていないかを行番号で示してくれる列も表示されます。
$ coverage report -m
Name Stmts Miss Cover Missing
------------------------------------------------------
goodbye.py 9 3 67% 12-14
greeting.py 22 12 45% 5, 12-13, 16-17, 20-21, 25-29
tests/__init__.py 0 0 100%
tests/test_goodbye.py 7 0 100%
tests/test_greeting.py 7 0 100%
------------------------------------------------------
TOTAL 45 15 67%
「coverage report」のオプションも色々あります。
$ coverage help report
Usage: coverage report [options] [modules]
Report coverage statistics on modules.
Options:
--contexts=REGEX1,REGEX2,...
Only display data from lines covered in the given
contexts. Accepts Python regexes, which must be
quoted.
--data-file=INFILE Read coverage data for report generation from this
file. Defaults to '.coverage'. [env: COVERAGE_FILE]
--fail-under=MIN Exit with a status of 2 if the total coverage is less
than MIN.
--format=FORMAT Output format, either text (default), markdown, or
total.
-i, --ignore-errors Ignore errors while reading source files.
--include=PAT1,PAT2,...
Include only files whose paths match one of these
patterns. Accepts shell-style wildcards, which must be
quoted.
--omit=PAT1,PAT2,... Omit files whose paths match one of these patterns.
Accepts shell-style wildcards, which must be quoted.
--precision=N Number of digits after the decimal point to display
for reported coverage percentages.
--sort=COLUMN Sort the report by the named column: name, stmts,
miss, branch, brpart, or cover. Default is name.
-m, --show-missing Show line numbers of statements in each module that
weren't executed.
--skip-covered Skip files with 100% coverage.
--no-skip-covered Disable --skip-covered.
--skip-empty Skip files with no code.
--debug=OPTS Debug options, separated by commas. [env:
COVERAGE_DEBUG]
-h, --help Get help on this command.
--rcfile=RCFILE Specify configuration file. By default '.coveragerc',
'setup.cfg', 'tox.ini', and 'pyproject.toml' are
tried. [env: COVERAGE_RCFILE]
Full documentation is at https://coverage.readthedocs.io/en/7.2.3
以下のレポートを見る限り、レポートに含めなくても良いファイルがあったりします。
Name Stmts Miss Cover
--------------------------------------------
goodbye.py 9 3 67%
greeting.py 22 12 45%
tests/__init__.py 0 0 100%
tests/test_goodbye.py 7 0 100%
tests/test_greeting.py 7 0 100%
--------------------------------------------
TOTAL 45 15 67%
例えば「__init__.py」や「test_....py」などです。
これらはテストを実行する際の「coverage run」の時点で除外することができますが、「coverage report」コマンドのオプションでも「含める・含めない」ファイルを指定できます。
ファイルを指定することで表示したいファイルのみ表示されます。
$ coverage report --include=goodbye.py,greeting.py
Name Stmts Miss Cover
---------------------------------
goodbye.py 9 3 67%
greeting.py 22 12 45%
---------------------------------
TOTAL 31 15 52%
レポートの表示で除外したいファイルがある場合は、相対パスで指定します。
$ coverage report --omit=tests/__init__.py
Name Stmts Miss Cover
--------------------------------------------
goodbye.py 9 3 67%
greeting.py 22 12 45%
tests/test_goodbye.py 7 0 100%
tests/test_greeting.py 7 0 100%
--------------------------------------------
TOTAL 45 15 67%
複数の除外したいファイルがある場合は正規表現を使って検索することができます。
$ coverage report --omit=tests/*
Name Stmts Miss Cover
---------------------------------
goodbye.py 9 3 67%
greeting.py 22 12 45%
---------------------------------
TOTAL 31 15 52%
カバー率(Cover)が100%のファイル以外を表示する場合。
$ coverage report --skip-covered
Name Stmts Miss Cover
---------------------------------
goodbye.py 9 3 67%
greeting.py 22 12 45%
---------------------------------
TOTAL 45 15 67%
3 files skipped due to complete coverage.
コードが書かれていないファイル以外を表示します。
$ coverage report --skip-empty
Name Stmts Miss Cover
--------------------------------------------
goodbye.py 9 3 67%
greeting.py 22 12 45%
tests/test_goodbye.py 7 0 100%
tests/test_greeting.py 7 0 100%
--------------------------------------------
TOTAL 45 15 67%
1 empty file skipped.
オプションは組み合わせて使えるので、柔軟にファイルの表示を行うことができます。
Webブラウザ上でカバレッジデータのレポートを表示するには、カバレッジデータを元にHTMLファイルを生成します。
以下のディレクトリ構造上で「coverage run」によりテストランのデータを収集したとします。
.
├── .venv
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
$ coverage run -m unittest
するとカレントディレクトリには「.coverage」というカバレッジデータが作成されます。
.
├── .coverage # new
├── .venv
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
カバレッジデータを元に生成するHTMLレポートを作成するには、「coverage html」コマンドを実行します。
$ coverage html
Wrote HTML report to htmlcov/index.html
コンソールにも表示しているように、カレントディレクトリに「htmlcov」ディレクトリが作成され、「index.html」が配置されています。
.
├── .coverage
├── .venv
├── goodbye.py
├── greeting.py
├── htmlcov # new
│ ├── .gitignore
│ ├── coverage_html.js
│ ├── d_a44f0ac069e85531___init___py.html
│ ├── d_a44f0ac069e85531_test_goodbye_py.html
│ ├── d_a44f0ac069e85531_test_greeting_py.html
│ ├── favicon_32.png
│ ├── goodbye_py.html
│ ├── greeting_py.html
│ ├── index.html
│ ├── keybd_closed.png
│ ├── keybd_open.png
│ ├── status.json
│ └── style.css
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
作成された「index.html」をブラウザで開くにはWindowsの場合ですとエクスプローラーかコマンドでリンクを作ってアクセスします。
以下はbashコマンドでリンクを作ってレポートを開いた例です。
※Windows Subsystem for Linuxの例
# readlinkコマンドでindex.htmlの絶対パスをlink変数に格納
$ link=`readlink -f htmlcov/index.html`
# wslpathコマンドでWindowsパスに変換してlink_win変数に格納
$ link_win=`wslpath -m $link`
# echoコマンドで「file:///」とlink_win変数を結合して表示する
$ echo file:///$link_win
file:///C:/Users/warik/Documents/PYTHON/cov/htmlcov/index.html
上記を行うと、ターミナルからブラウザへリンクすることができます。
アクセスされると、以下のようにレポートが可視化され各ファイルへアクセスできるようになります。
「greeting.py」にアクセスしてみると、カバー範囲等を確認することができます。
「coverage html」コマンドのオプションは以下です。
$ coverage help html
Usage: coverage html [options] [modules]
Create an HTML report of the coverage of the files. Each file gets its own
page, with the source decorated to show executed, excluded, and missed lines.
Options:
--contexts=REGEX1,REGEX2,...
Only display data from lines covered in the given
contexts. Accepts Python regexes, which must be
quoted.
-d DIR, --directory=DIR
Write the output files to DIR.
--data-file=INFILE Read coverage data for report generation from this
file. Defaults to '.coverage'. [env: COVERAGE_FILE]
--fail-under=MIN Exit with a status of 2 if the total coverage is less
than MIN.
-i, --ignore-errors Ignore errors while reading source files.
--include=PAT1,PAT2,...
Include only files whose paths match one of these
patterns. Accepts shell-style wildcards, which must be
quoted.
--omit=PAT1,PAT2,... Omit files whose paths match one of these patterns.
Accepts shell-style wildcards, which must be quoted.
--precision=N Number of digits after the decimal point to display
for reported coverage percentages.
-q, --quiet Don't print messages about what is happening.
--show-contexts Show contexts for covered lines.
--skip-covered Skip files with 100% coverage.
--no-skip-covered Disable --skip-covered.
--skip-empty Skip files with no code.
--title=TITLE A text string to use as the title on the HTML.
--debug=OPTS Debug options, separated by commas. [env:
COVERAGE_DEBUG]
-h, --help Get help on this command.
--rcfile=RCFILE Specify configuration file. By default '.coveragerc',
'setup.cfg', 'tox.ini', and 'pyproject.toml' are
tried. [env: COVERAGE_RCFILE]
Full documentation is at https://coverage.readthedocs.io/en/7.2.3
デフォルトでは「htmlcov」というHTMLレポートのディレクトリが作成sれますが、オプションを使って「covrepo」というHTMLレポートのディレクトリを作成します。
$ coverage html -d covrepo
Wrote HTML report to covrepo/index.html
.
├── .coverage
├── .venv
├── covrepo # new
│ ├── ...
│ ├── index.html
│ ├── ...
├── goodbye.py
├── greeting.py
└── tests
├── __init__.py
├── test_goodbye.py
└── test_greeting.py
$ coverage html --omit=tests/*
Wrote HTML report to htmlcov/index.html
$ coverage html --title='Greeting report'
Wrote HTML report to htmlcov/index.html
他のも色々とありますが、「coverage report」コマンドのオプションと共通する部分が多いので試してみて下さい。
それでは以上となります。
最後までご覧いただきありがとうございました。