Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Add `--no-split` to disable splitting even when `--splits` and `--group` are configured in pytest's `addopts`.

### Fixed
- Fix malformed bullet points rendering in GitHub Pages documentation

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ pytest --splits 3 --group 2
pytest --splits 3 --group 3
```

To disable splitting for a single run, use `--no-split`. This overrides `--splits`
and `--group`, including values configured in pytest's `addopts`:
```sh
pytest --no-split
```

Other pytest test selection options still apply. You can also combine `--no-split`
with `--store-durations` to refresh duration information without splitting the suite.

Time goes by, new tests are added and old ones are removed/renamed during development. No worries!
`pytest-split` assumes average test execution time (calculated based on the stored information) for every test which does not have duration information stored.
Thus, there's no need to store durations after changing the test suite.
Expand Down
10 changes: 9 additions & 1 deletion src/pytest_split/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ def pytest_addoption(parser: "Parser") -> None:
type=int,
help="The group of tests that should be executed (first one is 1)",
)
group.addoption(
"--no-split",
action="store_true",
help="Disable test splitting, overriding --splits and --group.",
)
group.addoption(
"--splitting-algorithm",
dest="splitting_algorithm",
Expand All @@ -79,6 +84,9 @@ def pytest_cmdline_main(config: "Config") -> "int | ExitCode | None":
"""
Validate options.
"""
if config.option.no_split:
return None

group = config.getoption("group")
splits = config.getoption("splits")

Expand All @@ -104,7 +112,7 @@ def pytest_configure(config: "Config") -> None:
"""
Enable the plugins we need.
"""
if config.option.splits and config.option.group:
if not config.option.no_split and config.option.splits and config.option.group:
config.pluginmanager.register(PytestSplitPlugin(config), "pytestsplitplugin")

if config.option.store_durations:
Expand Down
55 changes: 55 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,61 @@ def test_it_splits_with_other_collect_hooks(self, testdir, durations_path):
assert _passed_test_names(result) == expected_tests


class TestNoSplit:
@pytest.mark.parametrize("algorithm", Algorithms.names())
def test_overrides_configured_splitting(self, example_suite, algorithm):
example_suite.makeini(
f"[pytest]\naddopts = --splits 2 --group 1 --splitting-algorithm {algorithm}"
)

result = example_suite.runpytest("--no-split")

result.assert_outcomes(passed=EXAMPLE_SUITE_TEST_COUNT)
assert "[pytest-split]" not in result.stdout.str()

@pytest.mark.parametrize(
"addopts",
[
"",
"--splits 2",
"--group 1",
"--splits 0 --group 1",
"--splits 2 --group 3",
],
)
def test_ignores_unused_split_configuration(self, example_suite, addopts):
example_suite.makeini(f"[pytest]\naddopts = {addopts}")

result = example_suite.runpytest("--no-split")

result.assert_outcomes(passed=EXAMPLE_SUITE_TEST_COUNT)

def test_preserves_other_test_selection(self, example_suite):
example_suite.makeini("[pytest]\naddopts = --splits 2 --group 2")

result = example_suite.runpytest("--no-split", "-k", "test_1")

result.assert_outcomes(passed=2)
result.stdout.fnmatch_lines(
["*collected 10 items / 8 deselected / 2 selected*"]
)

def test_still_stores_durations(self, example_suite, durations_path):
example_suite.makeini("[pytest]\naddopts = --splits 2 --group 1")

result = example_suite.runpytest(
"--no-split", "--store-durations", "--durations-path", durations_path
)

result.assert_outcomes(passed=EXAMPLE_SUITE_TEST_COUNT)
with open(durations_path) as f:
durations = json.load(f)
assert set(durations) == {
f"test_still_stores_durations.py::test_{num}"
for num in range(1, EXAMPLE_SUITE_TEST_COUNT + 1)
}


class TestRaisesUsageErrors:
def test_returns_nonzero_when_group_but_not_splits(self, example_suite, capsys):
result = example_suite.inline_run("--group", "1")
Expand Down