[{"data":1,"prerenderedAt":926},["ShallowReactive",2],{"page-\u002Fadvanced-pytest-architecture-configuration\u002Fadvanced-parametrization-techniques\u002Fmarking-single-parametrized-cases-with-pytest-param\u002F":3},{"id":4,"title":5,"body":6,"description":889,"extension":890,"meta":891,"navigation":110,"path":922,"seo":923,"stem":924,"__hash__":925},"content\u002Fadvanced-pytest-architecture-configuration\u002Fadvanced-parametrization-techniques\u002Fmarking-single-parametrized-cases-with-pytest-param\u002Findex.md","Marking Single Parametrized Cases with pytest.param",{"type":7,"value":8,"toc":878},"minimark",[9,18,46,51,84,88,262,270,374,378,404,413,417,473,477,480,490,500,509,626,630,641,644,648,651,714,721,727,730,787,791,800,818,837,841,869,874],[10,11,12,13,17],"p",{},"Parametrized tests are where most of a suite's input coverage lives, and they are also where known problems most often go to be forgotten. Most parametrized cases should pass, but a realistic list usually contains a few that should not: an input that exposes a known bug, a combination unsupported on one platform, a case too slow for the fast suite. Removing those cases hides the knowledge; leaving them unmarked makes the test red. ",[14,15,16],"code",{},"pytest.param"," attaches marks to one case without affecting its siblings, so the known gap stays in the list, visible in every report, with its reason attached.",[10,19,20,21,24,25,27,28,30,31,34,35,38,39,42,43,45],{},"The feature is small and its value is almost entirely in how it is used. A strict ",[14,22,23],{},"xfail"," with a reason that names a ticket is documentation that enforces itself; a non-strict ",[14,26,23],{}," with no reason is a case that quietly stopped meaning anything. This guide covers the mechanics, the choice between ",[14,29,23],{},", ",[14,32,33],{},"skip"," and deleting the case, and the configuration that keeps a suite's register of known gaps accurate without anyone having to maintain it by hand. The same techniques apply whether the parameters come from a decorator, from fixture ",[14,36,37],{},"params",", or from ",[14,40,41],{},"pytest_generate_tests",", since all three accept ",[14,44,16],{}," in the same way.",[47,48,50],"h2",{"id":49},"prerequisites","Prerequisites",[52,53,54,61,73],"ul",{},[55,56,57,60],"li",{},[14,58,59],{},"pytest >= 8.0",".",[55,62,63,64,67,68,60],{},"Registered custom markers with ",[14,65,66],{},"--strict-markers",", from ",[69,70,72],"a",{"href":71},"\u002Fadvanced-pytest-architecture-configuration\u002Fpytest-configuration-best-practices\u002Fpytest-markers-for-conditional-test-execution\u002F","pytest markers for conditional test execution",[55,74,75,76,79,80,60],{},"The basics of ",[14,77,78],{},"@pytest.mark.parametrize"," from ",[69,81,83],{"href":82},"\u002Fadvanced-pytest-architecture-configuration\u002Fadvanced-parametrization-techniques\u002F","advanced parametrization techniques",[47,85,87],{"id":86},"solution","Solution",[89,90,95],"pre",{"className":91,"code":92,"language":93,"meta":94,"style":94},"language-python shiki shiki-themes github-light github-dark","import sys\n\nimport pytest\n\n\n@pytest.mark.parametrize(\n    (\"raw\", \"expected\"),\n    [\n        pytest.param(\"2026-09-18\", (2026, 9, 18), id=\"iso-date\"),\n        pytest.param(\"18\u002F09\u002F2026\", (2026, 9, 18), id=\"uk-date\"),\n        pytest.param(\n            \"2026-02-30\", None, id=\"impossible-date\",\n            # Known bug: strict so the case FAILS the day the bug is fixed.\n            marks=pytest.mark.xfail(strict=True, reason=\"BUG-412: accepts Feb 30\"),\n        ),\n        pytest.param(\n            \"2026-09-18T00:00:00+14:00\", (2026, 9, 18), id=\"plus-14-offset\",\n            marks=pytest.mark.skipif(sys.platform == \"win32\",\n                                     reason=\"tzdata lacks +14 on Windows runners\"),\n        ),\n        pytest.param(\n            \"𝟚𝟘𝟚𝟞-𝟘𝟡-𝟙𝟠\", (2026, 9, 18), id=\"math-digits\",\n            marks=pytest.mark.slow,     # selected by -m slow, excluded by -m \"not slow\"\n        ),\n    ],\n)\ndef test_parse_date(raw, expected):\n    assert parse_date(raw) == expected\n","python","",[14,96,97,105,112,118,123,128,134,140,146,152,158,164,170,176,182,188,193,199,205,211,216,221,227,233,238,244,250,256],{"__ignoreMap":94},[98,99,102],"span",{"class":100,"line":101},"line",1,[98,103,104],{},"import sys\n",[98,106,108],{"class":100,"line":107},2,[98,109,111],{"emptyLinePlaceholder":110},true,"\n",[98,113,115],{"class":100,"line":114},3,[98,116,117],{},"import pytest\n",[98,119,121],{"class":100,"line":120},4,[98,122,111],{"emptyLinePlaceholder":110},[98,124,126],{"class":100,"line":125},5,[98,127,111],{"emptyLinePlaceholder":110},[98,129,131],{"class":100,"line":130},6,[98,132,133],{},"@pytest.mark.parametrize(\n",[98,135,137],{"class":100,"line":136},7,[98,138,139],{},"    (\"raw\", \"expected\"),\n",[98,141,143],{"class":100,"line":142},8,[98,144,145],{},"    [\n",[98,147,149],{"class":100,"line":148},9,[98,150,151],{},"        pytest.param(\"2026-09-18\", (2026, 9, 18), id=\"iso-date\"),\n",[98,153,155],{"class":100,"line":154},10,[98,156,157],{},"        pytest.param(\"18\u002F09\u002F2026\", (2026, 9, 18), id=\"uk-date\"),\n",[98,159,161],{"class":100,"line":160},11,[98,162,163],{},"        pytest.param(\n",[98,165,167],{"class":100,"line":166},12,[98,168,169],{},"            \"2026-02-30\", None, id=\"impossible-date\",\n",[98,171,173],{"class":100,"line":172},13,[98,174,175],{},"            # Known bug: strict so the case FAILS the day the bug is fixed.\n",[98,177,179],{"class":100,"line":178},14,[98,180,181],{},"            marks=pytest.mark.xfail(strict=True, reason=\"BUG-412: accepts Feb 30\"),\n",[98,183,185],{"class":100,"line":184},15,[98,186,187],{},"        ),\n",[98,189,191],{"class":100,"line":190},16,[98,192,163],{},[98,194,196],{"class":100,"line":195},17,[98,197,198],{},"            \"2026-09-18T00:00:00+14:00\", (2026, 9, 18), id=\"plus-14-offset\",\n",[98,200,202],{"class":100,"line":201},18,[98,203,204],{},"            marks=pytest.mark.skipif(sys.platform == \"win32\",\n",[98,206,208],{"class":100,"line":207},19,[98,209,210],{},"                                     reason=\"tzdata lacks +14 on Windows runners\"),\n",[98,212,214],{"class":100,"line":213},20,[98,215,187],{},[98,217,219],{"class":100,"line":218},21,[98,220,163],{},[98,222,224],{"class":100,"line":223},22,[98,225,226],{},"            \"𝟚𝟘𝟚𝟞-𝟘𝟡-𝟙𝟠\", (2026, 9, 18), id=\"math-digits\",\n",[98,228,230],{"class":100,"line":229},23,[98,231,232],{},"            marks=pytest.mark.slow,     # selected by -m slow, excluded by -m \"not slow\"\n",[98,234,236],{"class":100,"line":235},24,[98,237,187],{},[98,239,241],{"class":100,"line":240},25,[98,242,243],{},"    ],\n",[98,245,247],{"class":100,"line":246},26,[98,248,249],{},")\n",[98,251,253],{"class":100,"line":252},27,[98,254,255],{},"def test_parse_date(raw, expected):\n",[98,257,259],{"class":100,"line":258},28,[98,260,261],{},"    assert parse_date(raw) == expected\n",[89,263,268],{"className":264,"code":266,"language":267,"meta":94},[265],"language-text","test_parse_date[iso-date]         PASSED\ntest_parse_date[uk-date]          PASSED\ntest_parse_date[impossible-date]  XFAIL  (BUG-412: accepts Feb 30)\ntest_parse_date[plus-14-offset]   SKIPPED (tzdata lacks +14 on Windows runners)\ntest_parse_date[math-digits]      PASSED\n","text",[14,269,266],{"__ignoreMap":94},[271,272,275,370],"figure",{"className":273},[274],"diagram",[276,277,284,285,284,289,284,293,284,301,284,310,284,320,284,326,284,332,284,336,284,342,284,347,284,351,284,356,284,362,284,366],"svg",{"viewBox":278,"role":279,"ariaLabelledBy":280,"xmlns":283},"0 0 820 262","img",[281,282],"pp-t","pp-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[286,287,288],"title",{"id":281},"Five cases, three kinds of mark",[290,291,292],"desc",{"id":282},"A parametrized list of five date inputs. Two plain cases run normally. One impossible date carries a strict xfail naming a bug. One timezone case carries a platform skipif. One Unicode-digit case carries a custom slow mark used for selection. Each mark affects only its own case.",[294,295],"rect",{"x":296,"y":296,"width":297,"height":298,"rx":299,"fill":300},"0","820","262","14","#fffdf8",[267,302,309],{"x":303,"y":304,"textAnchor":305,"fontSize":306,"fontWeight":307,"fill":308},"410","28","middle","16","700","#3d405b","Each mark belongs to one case, with its reason",[294,311],{"x":312,"y":313,"width":314,"height":315,"rx":316,"fill":317,"stroke":318,"strokeWidth":319},"26","48","768","34","8","#e6f0ea","#81b29a","1.8",[267,321,325],{"x":322,"y":323,"fontSize":324,"fill":308},"46","70","11.5","iso-date · uk-date — no mark, run normally",[294,327],{"x":312,"y":328,"width":314,"height":322,"rx":316,"fill":329,"stroke":330,"strokeWidth":331},"92","#fbe9e3","#e07a5f","2",[267,333,335],{"x":322,"y":334,"fontSize":324,"fontWeight":307,"fill":308},"112","impossible-date — xfail(strict=True, \"BUG-412\")",[267,337,341],{"x":322,"y":338,"fontSize":339,"fill":340},"129","10.5","#8f3d22","fails the build the day the bug is fixed, so the mark gets removed",[294,343],{"x":312,"y":344,"width":314,"height":322,"rx":316,"fill":345,"stroke":346,"strokeWidth":331},"148","#f7f0da","#f2cc8f",[267,348,350],{"x":322,"y":349,"fontSize":324,"fontWeight":307,"fill":308},"168","plus-14-offset — skipif(win32, \"tzdata lacks +14\")",[267,352,355],{"x":322,"y":353,"fontSize":339,"fill":354},"185","#8a5a00","skipped only where the condition holds; runs everywhere else",[294,357],{"x":312,"y":358,"width":314,"height":359,"rx":316,"fill":360,"stroke":308,"strokeWidth":361},"204","42","#f4f1de","1.6",[267,363,365],{"x":322,"y":364,"fontSize":324,"fontWeight":307,"fill":308},"224","math-digits — mark.slow",[267,367,369],{"x":322,"y":368,"fontSize":339,"fill":308},"239","excluded by -m \"not slow\", selected by -m slow",[371,372,373],"figcaption",{},"The report shows every case, including the ones that are not passing, with the reason in the same line. Nothing about the gap is hidden.",[47,375,377],{"id":376},"why-this-works","Why this works",[10,379,380,382,383,386,387,389,390,392,393,395,396,399,400,403],{},[14,381,16],{}," wraps a value together with an id and a set of marks. When ",[14,384,385],{},"parametrize"," generates items, it applies the wrapped marks to the item for that value alone, so the effect of an ",[14,388,23],{},", a ",[14,391,33],{}," or a custom mark is scoped exactly to one case. Everything else about the mark behaves as it would on a whole test: ",[14,394,23],{}," outcomes are reported as XFAIL or XPASS, ",[14,397,398],{},"skipif"," evaluates its condition at collection, and custom marks participate in ",[14,401,402],{},"-m"," selection.",[10,405,406,409,410,412],{},[14,407,408],{},"strict=True"," changes what an unexpected pass means. A non-strict ",[14,411,23],{}," that passes is reported as XPASS and the run stays green; a strict one that passes is a failure. That turns the mark from a note into a tripwire: the moment the bug is fixed, the build fails until someone removes the mark, which keeps the list of known gaps accurate without anyone having to remember to prune it.",[47,414,416],{"id":415},"edge-cases-and-failure-modes","Edge cases and failure modes",[52,418,419,430,440,452,465],{},[55,420,421,425,426,429],{},[422,423,424],"strong",{},"Non-strict xfail left in place."," It becomes XPASS after the fix and nobody notices. Set ",[14,427,428],{},"xfail_strict = true"," in configuration so every xfail is strict by default.",[55,431,432,435,436,439],{},[422,433,434],{},"Reasons that say nothing."," ",[14,437,438],{},"reason=\"broken\""," explains nothing to the next reader. Name the ticket, the constraint or the upstream issue.",[55,441,442,445,446,448,449,451],{},[422,443,444],{},"Skipping instead of xfailing a bug."," A skipped case is never executed, so it cannot tell you when the bug is fixed. Use ",[14,447,23],{}," for bugs and ",[14,450,33],{}," for combinations that genuinely cannot run.",[55,453,454,457,458,460,461,464],{},[422,455,456],{},"Unregistered custom marks."," Under ",[14,459,66],{}," an unregistered ",[14,462,463],{},"slow"," mark fails collection, which is the desired behaviour. Register it in configuration.",[55,466,467,470,471,60],{},[422,468,469],{},"Marks on the wrong level."," A mark on the whole test applies to every case. When only one case is affected, the mark belongs on ",[14,472,16],{},[47,474,476],{"id":475},"choosing-between-xfail-skip-and-removal","Choosing between xfail, skip and removal",[10,478,479],{},"Three responses to a case that should not pass right now look similar and mean very different things, and choosing correctly is most of the value of the feature.",[10,481,482,486,487,489],{},[422,483,484],{},[14,485,23],{}," means \"this is expected to fail because of a known defect, and we want to know when that changes\". The case still runs, so a fix is detected automatically — immediately with ",[14,488,408],{},". It is the right mark for bugs, for upstream issues awaiting a release, and for features partially implemented behind a flag.",[10,491,492,499],{},[422,493,494,496,497],{},[14,495,33],{}," or ",[14,498,398],{}," means \"this cannot meaningfully run here\". The case does not execute at all, so it can never report a change. It is the right mark for combinations that are genuinely unsupported — a platform without a required library, a database engine without a feature — and the wrong mark for bugs, because a skipped bug is a forgotten bug.",[10,501,502,505,506,508],{},[422,503,504],{},"Removal"," means \"this case is not a requirement\". If an input is no longer something the code should handle, deleting it is honest and marking it is not. A long-lived ",[14,507,23],{}," whose reason nobody can explain is often really a removal that nobody made.",[271,510,512,623],{"className":511},[274],[276,513,284,518,284,521,284,524,284,541,284,545,284,550,284,556,284,561,284,565,284,568,284,571,284,574,284,577,284,581,284,584,284,589,284,591,284,593,284,597,284,602,284,606,284,608,284,611,284,614,284,616,284,619],{"viewBox":514,"role":279,"ariaLabelledBy":515,"xmlns":283},"0 0 800 244",[516,517],"choose-t","choose-d",[286,519,520],{"id":516},"Deciding how to handle a case that should not pass now",[290,522,523],{"id":517},"A decision path. If the case fails because of a defect that will be fixed, use a strict xfail so the fix is detected. If the case cannot meaningfully run in some environment, use skipif with that condition. If the input is no longer a requirement at all, delete the case rather than marking it.",[525,526,527,528,284],"defs",{},"\n    ",[529,530,537],"marker",{"id":531,"viewBox":532,"refX":533,"refY":534,"markerWidth":535,"markerHeight":535,"orient":536},"choose-a","0 0 10 10","9","5","7","auto-start-reverse",[538,539],"path",{"d":540,"fill":308},"M0 0 L10 5 L0 10 z",[294,542],{"x":296,"y":296,"width":543,"height":544,"rx":299,"fill":300},"800","244",[267,546,549],{"x":547,"y":304,"textAnchor":305,"fontSize":548,"fontWeight":307,"fill":308},"400","15.5","Why is this case not passing?",[294,551],{"x":312,"y":552,"width":553,"height":554,"rx":555,"fill":360,"stroke":308,"strokeWidth":361},"56","236","68","11",[267,557,560],{"x":558,"y":559,"textAnchor":305,"fontSize":324,"fontWeight":307,"fill":308},"144","84","a defect we will fix",[267,562,564],{"x":558,"y":563,"textAnchor":305,"fontSize":555,"fill":308},"104","bug, upstream issue",[294,566],{"x":567,"y":552,"width":553,"height":554,"rx":555,"fill":360,"stroke":308,"strokeWidth":361},"282",[267,569,570],{"x":547,"y":559,"textAnchor":305,"fontSize":324,"fontWeight":307,"fill":308},"cannot run here",[267,572,573],{"x":547,"y":563,"textAnchor":305,"fontSize":555,"fill":308},"platform, engine, library",[294,575],{"x":576,"y":552,"width":553,"height":554,"rx":555,"fill":360,"stroke":308,"strokeWidth":361},"538",[267,578,580],{"x":579,"y":559,"textAnchor":305,"fontSize":324,"fontWeight":307,"fill":308},"656","no longer required",[267,582,583],{"x":579,"y":563,"textAnchor":305,"fontSize":555,"fill":308},"behaviour intentionally dropped",[100,585],{"x1":558,"y1":586,"x2":558,"y2":587,"stroke":308,"strokeWidth":361,"markerEnd":588},"128","150","url(#choose-a)",[100,590],{"x1":547,"y1":586,"x2":547,"y2":587,"stroke":308,"strokeWidth":361,"markerEnd":588},[100,592],{"x1":579,"y1":586,"x2":579,"y2":587,"stroke":308,"strokeWidth":361,"markerEnd":588},[294,594],{"x":312,"y":595,"width":553,"height":596,"rx":555,"fill":329,"stroke":330,"strokeWidth":331},"154","62",[267,598,601],{"x":558,"y":599,"textAnchor":305,"fontSize":600,"fontWeight":307,"fill":308},"180","12","xfail(strict=True)",[267,603,605],{"x":558,"y":604,"textAnchor":305,"fontSize":555,"fill":340},"200","runs; detects the fix",[294,607],{"x":567,"y":595,"width":553,"height":596,"rx":555,"fill":345,"stroke":346,"strokeWidth":331},[267,609,610],{"x":547,"y":599,"textAnchor":305,"fontSize":600,"fontWeight":307,"fill":308},"skipif(condition)",[267,612,613],{"x":547,"y":604,"textAnchor":305,"fontSize":555,"fill":354},"runs everywhere else",[294,615],{"x":576,"y":595,"width":553,"height":596,"rx":555,"fill":317,"stroke":318,"strokeWidth":331},[267,617,618],{"x":579,"y":599,"textAnchor":305,"fontSize":600,"fontWeight":307,"fill":308},"delete the case",[267,620,622],{"x":579,"y":604,"textAnchor":305,"fontSize":555,"fill":621},"#2a5f49","honest about requirements",[371,624,625],{},"The commonest mistake is the middle box used for the left-hand situation: a skipped bug, which will never report that it has been fixed.",[47,627,629],{"id":628},"marks-as-a-workflow-not-just-a-status","Marks as a workflow, not just a status",[10,631,632,633,637,638,640],{},"Used deliberately, per-case marks support a workflow that is otherwise awkward: writing the test for a bug ",[634,635,636],"em",{},"before"," fixing it. Add the failing input as a case with a strict ",[14,639,23],{}," naming the ticket, merge that on its own, and the suite now documents the bug precisely and stays green. When the fix lands in a later change, the strict mark forces the same change to remove it, so the fix and the removal of the known-failure marker are reviewed together.",[10,642,643],{},"That sequence has two advantages over fixing and testing in one change. The failing case is in the suite from the moment the bug is understood, so nobody can accidentally make it worse in the meantime. And the fix's pull request is smaller and easier to review, because the test that proves it was already reviewed on its own. Teams that adopt it find their bug tickets acquire a reliable link to the exact test that reproduces them, which is valuable long after the bug is closed — it is where the next person looking at similar behaviour will start.",[47,645,647],{"id":646},"keeping-the-list-of-known-gaps-honest","Keeping the list of known gaps honest",[10,649,650],{},"Marked cases are a register of known problems, and like any register it decays unless someone reviews it. A few lines of tooling make that review cheap.",[89,652,656],{"className":653,"code":654,"language":655,"meta":94,"style":94},"language-bash shiki shiki-themes github-light github-dark","# Every xfail and skip reason in the suite, with where it lives.\ngrep -rn \"xfail\\|skipif\\|pytest.mark.skip\" tests\u002F | grep -o 'reason=\"[^\"]*\"' | sort | uniq -c | sort -rn\n","bash",[14,657,658,664],{"__ignoreMap":94},[98,659,660],{"class":100,"line":101},[98,661,663],{"class":662},"sJ8bj","# Every xfail and skip reason in the suite, with where it lives.\n",[98,665,666,670,674,678,681,685,688,691,694,696,699,701,704,707,709,711],{"class":100,"line":107},[98,667,669],{"class":668},"sScJk","grep",[98,671,673],{"class":672},"sj4cs"," -rn",[98,675,677],{"class":676},"sZZnC"," \"xfail\\|skipif\\|pytest.mark.skip\"",[98,679,680],{"class":676}," tests\u002F",[98,682,684],{"class":683},"szBVR"," |",[98,686,687],{"class":668}," grep",[98,689,690],{"class":672}," -o",[98,692,693],{"class":676}," 'reason=\"[^\"]*\"'",[98,695,684],{"class":683},[98,697,698],{"class":668}," sort",[98,700,684],{"class":683},[98,702,703],{"class":668}," uniq",[98,705,706],{"class":672}," -c",[98,708,684],{"class":683},[98,710,698],{"class":668},[98,712,713],{"class":672}," -rn\n",[10,715,716,717,720],{},"Running the suite with ",[14,718,719],{},"-rxXs"," prints a short summary of every xfailed, xpassed and skipped item at the end of the run, which is the other view worth checking — it shows which marked cases actually ran and what happened to them.",[10,722,723,724,726],{},"Setting ",[14,725,428],{}," globally is the single most effective habit. It means an xfail can never silently turn into a pass: either the bug still exists and the case still fails, or it has been fixed and the build goes red until the mark is removed. The register stays accurate by construction rather than by diligence, which is the only kind of accuracy that survives a busy team.",[10,728,729],{},"A periodic look at the skip reasons is worth adding to that. Unlike xfails, skips never announce themselves, so a platform constraint that was lifted two releases ago will keep a case skipped until someone reads the list. Once a quarter, running the reason-count command above and asking of each skip whether its condition is still true takes ten minutes and usually removes two or three.",[271,731,733,782],{"className":732},[274],[276,734,284,739,284,742,284,745,284,748,284,751,284,756,284,760,284,764,284,768,284,771,284,774,284,778],{"viewBox":735,"role":279,"ariaLabelledBy":736,"xmlns":283},"0 0 800 234",[737,738],"xs-t","xs-d",[286,740,741],{"id":737},"Strict versus non-strict xfail once a bug is fixed",[290,743,744],{"id":738},"Two timelines after the bug behind an xfail is fixed. With a non-strict xfail, the case now passes and is reported as XPASS while the build stays green, so the stale mark persists indefinitely. With a strict xfail, the unexpected pass fails the build, prompting removal of the mark and keeping the known-gap register accurate.",[294,746],{"x":296,"y":296,"width":543,"height":747,"rx":299,"fill":300},"234",[267,749,750],{"x":547,"y":304,"textAnchor":305,"fontSize":548,"fontWeight":307,"fill":308},"What happens the day the bug is fixed",[294,752],{"x":312,"y":753,"width":754,"height":755,"rx":555,"fill":329,"stroke":330,"strokeWidth":331},"52","748","72",[267,757,759],{"x":322,"y":758,"fontSize":600,"fontWeight":307,"fill":308},"76","xfail (non-strict)",[267,761,763],{"x":322,"y":762,"fontSize":555,"fill":308},"98","case passes → XPASS → build green → mark stays for months",[267,765,767],{"x":322,"y":766,"fontSize":555,"fill":340},"116","the register now lists a bug that no longer exists",[294,769],{"x":312,"y":770,"width":754,"height":755,"rx":555,"fill":317,"stroke":318,"strokeWidth":331},"138",[267,772,601],{"x":322,"y":773,"fontSize":600,"fontWeight":307,"fill":308},"162",[267,775,777],{"x":322,"y":776,"fontSize":555,"fill":308},"184","case passes → FAILED [XPASS(strict)] → someone removes the mark",[267,779,781],{"x":322,"y":780,"fontSize":555,"fill":621},"202","the register stays accurate without anyone remembering to prune it",[371,783,784,786],{},[14,785,428],{}," in configuration makes the lower row the default for every xfail in the suite.",[47,788,790],{"id":789},"frequently-asked-questions","Frequently Asked Questions",[10,792,793,796,797,799],{},[422,794,795],{},"Should xfail on a parametrized case be strict?","\nYes, in almost every case. ",[14,798,408],{}," turns an unexpected pass into a failure, which is exactly the signal that a known bug has been fixed and the mark should be removed. A non-strict xfail that starts passing is reported as XPASS and is easy to miss for months.",[10,801,802,805,806,809,810,813,814,817],{},[422,803,804],{},"Can a mark on one case depend on the environment?","\nYes. ",[14,807,808],{},"pytest.mark.skipif"," and ",[14,811,812],{},"pytest.mark.xfail"," accept a condition, so ",[14,815,816],{},"pytest.param(value, marks=pytest.mark.skipif(sys.platform == \"win32\", reason=\"…\"))"," skips that one case only where the condition holds.",[10,819,820,823,824,826,827,829,830,833,834,836],{},[422,821,822],{},"How do I select only the marked cases?","\nCustom marks on ",[14,825,16],{}," participate in ",[14,828,402],{}," selection like any other mark. ",[14,831,832],{},"pytest -m slow"," runs only the cases, across all tests, that carry the ",[14,835,463],{}," mark — useful for keeping a few expensive inputs out of the fast suite.",[47,838,840],{"id":839},"related","Related",[52,842,843,849,856,862],{},[55,844,845,848],{},[69,846,847],{"href":82},"Advanced Parametrization Techniques"," — the parametrization model these marks attach to.",[55,850,851,855],{},[69,852,854],{"href":853},"\u002Fadvanced-pytest-architecture-configuration\u002Fadvanced-parametrization-techniques\u002Fgenerating-readable-test-ids\u002F","Generating Readable Test IDs"," — ids that make marked cases easy to find in reports.",[55,857,858,861],{},[69,859,860],{"href":71},"pytest Markers for Conditional Test Execution"," — registering and selecting custom marks.",[55,863,864,868],{},[69,865,867],{"href":866},"\u002Fadvanced-pytest-architecture-configuration\u002Foptimizing-test-discovery\u002Fdebugging-flaky-tests-with-pytest-rerunfailures\u002F","Debugging Flaky Tests with pytest-rerunfailures"," — why flakiness is not a reason for xfail.",[10,870,871,872],{},"← Back to ",[69,873,847],{"href":82},[875,876,877],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":94,"searchDepth":107,"depth":107,"links":879},[880,881,882,883,884,885,886,887,888],{"id":49,"depth":107,"text":50},{"id":86,"depth":107,"text":87},{"id":376,"depth":107,"text":377},{"id":415,"depth":107,"text":416},{"id":475,"depth":107,"text":476},{"id":628,"depth":107,"text":629},{"id":646,"depth":107,"text":647},{"id":789,"depth":107,"text":790},{"id":839,"depth":107,"text":840},"Apply xfail, skip and custom marks to individual parametrized cases with pytest.param: strict xfail, conditional skips, readable ids, and keeping known gaps visible.","md",{"slug":892,"type":893,"breadcrumb":894,"datePublished":895,"dateModified":895,"faq":896,"howto":903},"marking-single-parametrized-cases-with-pytest-param","article","pytest.param marks","2026-09-18",[897,899,901],{"q":795,"a":898},"Yes, in almost every case. strict=True turns an unexpected pass into a failure, which is exactly the signal that a known bug has been fixed and the mark should be removed. A non-strict xfail that starts passing is reported as XPASS and is easy to miss for months.",{"q":804,"a":900},"Yes. pytest.mark.skipif and pytest.mark.xfail accept a condition, so pytest.param(value, marks=pytest.mark.skipif(sys.platform == 'win32', reason='…')) skips that one case only where the condition holds.",{"q":822,"a":902},"Custom marks on pytest.param participate in -m selection like any other mark. pytest -m slow runs only the cases, across all tests, that carry the slow mark — useful for keeping a few expensive inputs out of the fast suite.",{"name":904,"description":905,"steps":906},"How to mark individual parametrized cases","Wrap the case in pytest.param with marks and an id, make xfails strict, and give every mark a reason.",[907,910,913,916,919],{"name":908,"text":909},"Wrap the case","Replace the bare value with pytest.param(value, id=..., marks=...).",{"name":911,"text":912},"Choose the mark","Use xfail for a known bug, skip or skipif for an unsupported combination, and a custom mark for selection.",{"name":914,"text":915},"Make xfail strict","Pass strict=True so the case fails loudly once the bug is fixed.",{"name":917,"text":918},"Give every mark a reason","Include a reason that names the bug or constraint so the report explains itself.",{"name":920,"text":921},"Review marks periodically","List xfail and skip reasons across the suite and remove the ones that no longer apply.","\u002Fadvanced-pytest-architecture-configuration\u002Fadvanced-parametrization-techniques\u002Fmarking-single-parametrized-cases-with-pytest-param",{"title":5,"description":889},"advanced-pytest-architecture-configuration\u002Fadvanced-parametrization-techniques\u002Fmarking-single-parametrized-cases-with-pytest-param\u002Findex","It-4CRDldp_WhRupDUqtExPIAyDaTv_wXE2PJtzWn6A",1789718767545]