Skip to content

Commit

Permalink
Add the ability to write junit test results file for pytest runs.
Browse files Browse the repository at this point in the history
Problem

With the transition of pytest to the v2 engine, we lost the ability to write junit-xml results files when test runs.
This can't be solved by passing options thru directly to pytest since it expect a junit-xml result file name, which means we need a unique name for each target.

 Solution

Introduce the ability to calculate the junit-xml file name based on a user provided direcotry (option) and the test target name.

 Result

JUnit xml result files which can be processed by CI (CircleCI for example) so failing test can be seen w/o digging thru endless logs.
  • Loading branch information
asherf committed Apr 20, 2020
1 parent 107b833 commit 770f933
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 1 deletion.
14 changes: 13 additions & 1 deletion src/python/pants/backend/python/rules/pytest_runner.py
Expand Up @@ -3,6 +3,7 @@

import functools
from dataclasses import dataclass
from pathlib import Path
from typing import Any, List, Optional, Tuple, Union, cast

from pants.backend.python.rules.importable_python_sources import ImportablePythonSources
Expand Down Expand Up @@ -59,6 +60,7 @@ class TestTargetSetup:
args: Tuple[str, ...]
input_files_digest: Digest
timeout_seconds: Optional[int]
results_file_path: Optional[str]

# Prevent this class from being detected by pytest as a test class.
__test__ = False
Expand Down Expand Up @@ -210,11 +212,18 @@ async def setup_pytest_for_target(
coverage_args.extend(["--cov", package])

specified_source_file_names = sorted(specified_source_files.snapshot.files)
results_dir = test_options.values.results_dir
results_file_path = (
(Path(results_dir) / f"{config.address.path_safe_spec}.xml").absolute().as_posix()
if results_dir
else None
)
return TestTargetSetup(
test_runner_pex=test_runner_pex,
args=(*pytest.options.args, *coverage_args, *specified_source_file_names),
input_files_digest=merged_input_files,
timeout_seconds=config.timeout.calculate_from_global_options(pytest),
results_file_path=results_file_path,
)


Expand All @@ -228,7 +237,10 @@ async def run_python_test(
test_options: TestOptions,
) -> TestResult:
"""Runs pytest for one target."""
env = {"PYTEST_ADDOPTS": f"--color={'yes' if global_options.options.colors else 'no'}"}
add_opts = [f"--color={'yes' if global_options.options.colors else 'no'}"]
if test_setup.results_file_path:
add_opts.append(f"--junitxml={test_setup.results_file_path}")
env = {"PYTEST_ADDOPTS": " ".join(add_opts)}
run_coverage = test_options.values.run_coverage
request = test_setup.test_runner_pex.create_execute_request(
python_setup=python_setup,
Expand Down
7 changes: 7 additions & 0 deletions src/python/pants/core/goals/test.py
Expand Up @@ -35,6 +35,7 @@
TargetWithOrigin,
)
from pants.engine.unions import UnionMembership, union
from pants.option.custom_types import dir_option

# TODO(#6004): use proper Logging singleton, rather than static logger.
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -230,6 +231,12 @@ def register_options(cls, register) -> None:
help="If a coverage report file is generated, open it on the local system if the "
"system supports this.",
)
register(
"--results-dir",
type=dir_option,
default=None,
help="Directory to store tests results files",
)


class Test(Goal):
Expand Down

0 comments on commit 770f933

Please sign in to comment.