Skip to main content

🚦 Pytest Collection vs Marks

Why -m does not prevent Pytest from importing failing tests

When using Pytest, one of the most confusing behaviors for new (and even experienced) users is:

Filtering by mark (pytest -m mymark) does not prevent Pytest from running collection.

This often leads to a surprising failure:

  • You run a test with a specific mark
  • But Pytest still tries to import all other test modules
  • Some unrelated modules fail during import
  • Pytest aborts before your intended test even runs

This article explains why this happens and how to properly control what Pytest collects.


🔄 The Two-Phase Execution Model

Pytest always runs in two phases:

1. Discovery & Collection (import phase)

Pytest:

  • Walks the filesystem
  • Finds all files matching the test pattern
  • Imports every test module
  • Executes module-level code and loads fixtures
  • Loads conftest.py files
  • Builds an internal tree of test functions & classes

If any test file has:

  • bad imports
  • missing dependencies
  • syntax errors
  • exceptions in module-level code

… Pytest stops with a collection error, even if you didn’t intend to run that test.


2. Selection & Execution

Only after collection succeeds does Pytest apply:

  • -m markers
  • -k keyword expressions
  • Test name selection
  • Skips / xfails
  • Test ordering plugins

This means:

Marks filter test execution, not test collection.


❗ Why pytest -m mymark Does Not Help

You might expect this to run only tests with a specific mark:

pytest -m datasources_binance

But Pytest’s sequence is:

  1. Collect everything
  2. Apply mark filter
  3. Run filtered tests

If step 1 fails → step 2 never happens.

That’s why a completely unrelated broken test file can block you from running your marked tests.


✔️ What Does Prevent Collection?

Only path-based selection reduces discovery:

Run a single file:

pytest tests/data/sources/binance/test_klines_time_range.py

Run a single test:

pytest tests/data/sources/binance/test_klines_time_range.py::test_compute_time_range_live

Run only a folder:

pytest tests/data/sources/binance

Ignore problematic folders (persistent fix):

pytest.ini:

[pytest]
addopts =
--ignore=tests/data/candle-quality-py
--ignore=tests/data/resampling
--ignore=tests/data/sources/drift
--ignore=tests/integration

Now Pytest won’t even look inside those folders—collection errors disappear.


🔍 Summary Table

ActionPrevents Collection?Explanation
pytest -m foo❌ NoFiltering happens after collection
pytest -k foo❌ NoKeyword selection also happens post-collection
pytest path/to/file.py✔️ YesPytest collects only this file
pytest path/to/folder✔️ YesPytest collects only in that folder
--ignore <dir>✔️ YesPytest never scans ignored dirs
Module-level skip (pytest.skip)❌ NoStill imported during collection

🧠 Mental Model Cheat Sheet

  • Markers ≠ import filters
  • -m filters runnable tests, not loadable modules
  • Collection failures happen before markers apply
  • To isolate test runs, always use file/folder paths or --ignore

This applies to all marks:

  • @pytest.mark.slow
  • @pytest.mark.binance
  • @pytest.mark.integration
  • etc.

None of them block Pytest from importing unrelated test modules.


🎯 Recommended Workflow for Large Repos

When actively developing:

Use path-based targeted runs

pytest tests/data/sources/binance/test_klines_time_range.py

Add opts to pytest.ini to filter out specific dirs

[pytest]
addopts = --ignore=tests/experimental

Only use -m to choose between collected tests

Markers are great for:

  • grouping slow tests
  • tagging integration tests
  • running Binance-related tests selectively
  • skipping flaky tests

But they’re not a substitute for controlling collection scope.


🏁 Final Takeaway

If you need to avoid collecting broken tests, use file/folder paths or ignore directives — not marks.

Markers help select what to run, paths help select what to load, and Pytest imports everything before it looks at marks.


If you want, I can turn this into a polished Docusaurus page with front-matter, side navigation category, and rich examples.