Pytest using pyproject.toml (#29)

# New Features
* Allow configuration of the unit test directory (default: `tests/unit`).
* Allow configuration of a `pyproject.toml` or `.coveragerc` file.
* Extract values from `pyproject.toml` or `.coveragerc`.

# Changes
* Jobs deriving from template job `CoverageCollection` need to specify their `.coveragerc` content and `pytest.ini` content in a `pyproject.toml` file or provide the job parameter `coverage_config` with the path to the `.coveragerc` file.

# Bug Fixes
*None*

-------------
* Closes #2.
This commit is contained in:
Unai Martinez-Corral
2021-12-25 23:40:50 +01:00
committed by GitHub
3 changed files with 72 additions and 13 deletions

View File

@@ -35,6 +35,16 @@ on:
required: false required: false
default: '-r tests/requirements.txt' default: '-r tests/requirements.txt'
type: string type: string
unittest_directory:
description: 'Path to the directory containing unit tests.'
required: false
default: 'tests/unit'
type: string
coverage_config:
description: 'Path to the .coveragerc file. Use pyproject.toml by default.'
required: false
default: 'pyproject.toml'
type: string
artifact: artifact:
description: 'Name of the coverage artifact.' description: 'Name of the coverage artifact.'
required: true required: true
@@ -62,27 +72,68 @@ jobs:
- name: 🗂 Install dependencies - name: 🗂 Install dependencies
run: | run: |
python -m pip install -U pip python -m pip install -U pip
python -m pip install tomli
python -m pip install ${{ inputs.requirements }} python -m pip install ${{ inputs.requirements }}
- name: 🔁 Extract configurations from pyproject.toml
id: getVariables
shell: python
run: |
from pathlib import Path
from tomli import load as tomli_load
htmlDirectory = 'htmlcov'
xmlFile = './coverage.xml'
coverageRC = "${{ inputs.coverage_config }}".strip()
# Read output paths from 'pyproject.toml' file
if coverageRC == "pyproject.toml":
pyProjectFile = Path("pyproject.toml")
if pyProjectFile.exists():
with pyProjectFile.open("rb") as file:
pyProjectSettings = tomli_load(file)
htmlDirectory = pyProjectSettings["tool"]["coverage"]["html"]["directory"]
xmlFile = pyProjectSettings["tool"]["coverage"]["xml"]["output"]
else:
print(f"File '{pyProjectFile}' not found and no ' .coveragerc' file specified.")
# Read output paths from '.coveragerc' file
elif len(coverageRC) > 0:
coverageRCFile = Path(coverageRC)
if coverageRCFile.exists():
with coverageRCFile.open("rb") as file:
coverageRCSettings = tomli_load(file)
htmlDirectory = coverageRCSettings["html"]["directory"]
xmlFile = coverageRCSettings["xml"]["output"]
else:
print(f"File '{coverageRCFile}' not found.")
print(f"::set-output name=coverage_report_html_directory::{htmlDirectory}")
print(f"::set-output name=coverage_report_xml::{xmlFile}")
print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}")
- name: Collect coverage - name: Collect coverage
continue-on-error: true continue-on-error: true
run: | run: |
python -m pytest -rA --cov=.. --cov-config=tests/.coveragerc tests/unit --color=yes [ 'x${{ inputs.coverage_config }}' != 'x' ] && PYCOV_ARGS='--cov-config=${{ inputs.coverage_config }}' || unset PYCOV_ARGS
python -m pytest -rA --cov=. $PYCOV_ARGS ${{ inputs.unittest_directory }} --color=yes
- name: Convert to cobertura format - name: Convert to cobertura format
run: coverage xml run: coverage xml
- name: Convert to HTML format - name: Convert to HTML format
run: | run: |
coverage html coverage html -d ${{ steps.getVariables.outputs.coverage_report_html_directory }}
rm htmlcov/.gitignore rm ${{ steps.getVariables.outputs.coverage_report_html_directory }}/.gitignore
- name: 📤 Upload 'Coverage Report' artifact - name: 📤 Upload 'Coverage Report' artifact
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: ${{ inputs.artifact }} name: ${{ inputs.artifact }}
path: htmlcov path: ${{ steps.getVariables.outputs.coverage_report_html_directory }}
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
@@ -90,7 +141,7 @@ jobs:
continue-on-error: true continue-on-error: true
uses: codecov/codecov-action@v1 uses: codecov/codecov-action@v1
with: with:
file: ./coverage.xml file: ${{ steps.getVariables.outputs.coverage_report_xml }}
flags: unittests flags: unittests
env_vars: PYTHON env_vars: PYTHON
@@ -99,4 +150,4 @@ jobs:
uses: codacy/codacy-coverage-reporter-action@master uses: codacy/codacy-coverage-reporter-action@master
with: with:
project-token: ${{ secrets.codacy_token }} project-token: ${{ secrets.codacy_token }}
coverage-reports: ./coverage.xml coverage-reports: ${{ steps.getVariables.outputs.coverage_report_xml }}

View File

@@ -34,6 +34,11 @@ on:
required: false required: false
default: '-r tests/requirements.txt' default: '-r tests/requirements.txt'
type: string type: string
unittest_directory:
description: 'Path to the directory containing unit tests.'
required: false
default: 'tests/unit'
type: string
artifact: artifact:
description: "Generate unit test report with junitxml and upload results as an artifact." description: "Generate unit test report with junitxml and upload results as an artifact."
required: false required: false
@@ -68,7 +73,7 @@ jobs:
- name: ☑ Run unit tests - name: ☑ Run unit tests
run: | run: |
[ 'x${{ inputs.artifact }}' != 'x' ] && PYTEST_ARGS='--junitxml=TestReport.xml' || unset PYTEST_ARGS [ 'x${{ inputs.artifact }}' != 'x' ] && PYTEST_ARGS='--junitxml=TestReport.xml' || unset PYTEST_ARGS
python -m pytest -rA tests/unit $PYTEST_ARGS --color=yes python -m pytest -rA ${{ inputs.unittest_directory }} $PYTEST_ARGS --color=yes
- name: 📤 Upload 'TestReport.xml' artifact - name: 📤 Upload 'TestReport.xml' artifact
if: inputs.artifact != '' if: inputs.artifact != ''

View File

@@ -95,13 +95,16 @@ As shown in the screenshot above, the expected order is:
- Global: - Global:
- [Parameters](.github/workflows/Parameters.yml): a workaround for the limitations to handle global variables in - [Parameters](.github/workflows/Parameters.yml): a workaround for the limitations to handle global variables in
GitHub Actions workflows (see [actions/runner#480](https://github.com/actions/runner/issues/480)). GitHub Actions workflows (see [actions/runner#480](https://github.com/actions/runner/issues/480)).
It generates outputs with artifact names and job matrices to be used in other jobs. It generates outputs with artifact names and job matrices to be used in later running jobs.
- Code testing/analysis: - Code testing/analysis:
- [UnitTesting](.github/workflows/UnitTesting.yml): run unit test with `pytest` using multiple versions of Python, and - [UnitTesting](.github/workflows/UnitTesting.yml): run unit test with `pytest` using multiple versions of Python, and
optionally upload results as XML reports. optionally upload results as XML reports. Configuration options to `pytest` should be given via section
- [CoverageCollection](.github/workflows/CoverageCollection.yml): collect coverage data with `pytest` using a single `[tool.pytest.ini_options]` in a `pyproject.toml` file.
version of Python, generate HTML and Cobertura (XML) reports, upload the HTML report as an artifact, and upload the - [CoverageCollection](.github/workflows/CoverageCollection.yml): collect code coverage data (incl. branch coverage)
results to Codecov and Codacy. with `pytest`/`pytest-cov`/`coverage.py` using a single version of Python (latest). It generates HTML and Cobertura
(XML)reports, upload the HTML report as an artifact, and upload the test results to Codecov and Codacy. Configuration
options to `pytest` and `coverage.py` should be given via section `[tool.pytest.ini_options]` and `[tool.coverage.*]`
in a `pyproject.toml` file.
- [StaticTypeCheck](.github/workflows/StaticTypeCheck.yml): collect static type check result with `mypy`, and - [StaticTypeCheck](.github/workflows/StaticTypeCheck.yml): collect static type check result with `mypy`, and
optionally upload results as an HTML report. optionally upload results as an HTML report.
Example `commands`: Example `commands`:
@@ -128,7 +131,7 @@ As shown in the screenshot above, the expected order is:
mypy --html-report ../htmlmypy -p ToolName mypy --html-report ../htmlmypy -p ToolName
``` ```
- [VerifyDocs](.github/workflows/VerifyDocs.yml): extract code examples from the README and test. - [VerifyDocs](.github/workflows/VerifyDocs.yml): extract code examples from the README and test these code snippets.
- Packaging and releasing: - Packaging and releasing:
- [Release](.github/workflows/Release.yml): publish GitHub Release. - [Release](.github/workflows/Release.yml): publish GitHub Release.
- [Package](.github/workflows/Package.yml): generate source and wheel packages, and upload them as an artifact. - [Package](.github/workflows/Package.yml): generate source and wheel packages, and upload them as an artifact.