Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›pytest & Testing›Expected Failures — xfail

Parametrize and Marks

Expected Failures — xfail

The xfail marker allows you to document known bugs or incomplete features without failing your entire test suite. It provides a mechanism to acknowledge failures while keeping the testing process productive and transparent. You should use it when you want to track a specific issue without breaking the continuous integration pipeline.

Introduction to xfail

The xfail mark serves as a formal declaration that a test is expected to fail under certain conditions. Instead of allowing a failing test to disrupt your build status, pytest captures this failure and marks it as 'xfailed' (expected failure). This is incredibly useful during the development lifecycle when you encounter a bug that you cannot fix immediately but want to prevent from being forgotten. By using this marker, you explicitly communicate your intent to other developers, signaling that the failure is not an oversight but a conscious decision. The underlying engine intercepts the assertion failure and prevents the test report from flagging it as a red 'failed' result. This keeps your overall health dashboard accurate, ensuring that legitimate regressions are clearly distinguished from known, anticipated issues that you are currently tracking.

import pytest

@pytest.mark.xfail
def test_feature_not_ready():
    # This test will show as 'xfail' rather than 'failed'
    assert False, "Feature is still under development"

Conditional Expected Failures

In real-world applications, you often find that a test should only fail under specific circumstances, such as when running on a particular operating system or with a specific configuration. Pytest provides the 'condition' argument to the xfail marker, which takes a boolean expression. If this expression evaluates to true, the test will be treated as an expected failure. If it evaluates to false, the test will be executed as a standard test case. This flexibility is vital because it prevents you from silencing a test that should actually be passing in other environments. By tying the failure expectation to the environment, you maintain high confidence in your test coverage across diverse platforms while cleanly handling the limitations of specific setups that you do not wish to fix immediately.

import pytest
import sys

@pytest.mark.xfail(sys.platform == "win32", reason="Known bug on Windows")
def test_feature_compatibility():
    # Only expected to fail on Windows platforms
    assert 1 == 2, "Fails due to platform-specific library issue"

Strict xfail Handling

A common problem with expected failures is the 'false sense of security'—sometimes a bug is fixed, but you forget to remove the xfail marker. This results in an 'xpassed' state, where the test passes unexpectedly. To solve this, pytest includes the 'strict' parameter. When strict is set to True, any test marked as xfail that unexpectedly passes will be reported as a failure instead of an xpass. This is a best practice for maintaining a clean suite because it forces you to acknowledge that your assumptions about the test failing are no longer true. By enforcing strictness, you ensure that you are constantly alerted when your code improves beyond your expectations, preventing outdated markers from hiding potential problems or lingering in your codebase permanently.

import pytest

# strict=True means an unexpected pass will trigger a test failure
@pytest.mark.xfail(strict=True)
def test_bug_fix_is_applied():
    # If this passes, the test suite will fail, prompting you to remove the marker
    assert 1 == 1

Failing on Specific Exceptions

Often, you might expect a piece of code to fail, but only for a very specific reason, such as a known missing feature throwing a 'NotImplementedError'. You can use the 'raises' parameter to tell pytest exactly which exception you expect to catch. If the test fails for any other reason—for example, if a database connection error occurs or a syntax error happens—the test will still be marked as a regular failure. This is an advanced way to constrain the scope of your expected failure, ensuring that you are only ignoring the issues you intended to ignore. It prevents you from masking 'accidental' failures that arise from unrelated bugs, keeping your testing suite robust and precise even when dealing with code that is officially incomplete or unstable.

import pytest

@pytest.mark.xfail(raises=NotImplementedError)
def test_incomplete_service():
    # We only want to xfail if it hits this specific error
    raise NotImplementedError("Service not wired up yet")

Combining xfail with Parametrize

When testing many scenarios at once, it is common to find that some parameters result in expected failures while others work perfectly. By combining the 'parametrize' decorator with the 'xfail' marker, you can apply expected failure status to specific data subsets. This allows you to perform highly granular testing where your coverage is broad, but the 'known bad' cases are isolated. The key is to define your parameters and then use 'pytest.param' to decorate specific entries with the xfail marker. This avoids the need to write separate test functions for failing versus passing logic. It keeps your test file concise and readable, grouping related logic together while ensuring that the build remains stable despite the existence of documented issues in specific input ranges or data combinations.

import pytest

@pytest.mark.parametrize("val, expected", [
    (1, 1),
    pytest.param(2, 3, marks=pytest.mark.xfail(reason="Math is wrong here"))
])
def test_calculation(val, expected):
    assert val == expected

Key points

  • The xfail marker prevents known failures from triggering a full build failure in your testing environment.
  • Conditional xfail allows you to restrict expected failures to specific platforms or configurations.
  • Using strict=True forces a test to fail if it passes unexpectedly, helping you clean up outdated markers.
  • The raises argument ensures that only specific expected exceptions trigger the xfail state.
  • Parametrizing tests with xfail allows for granular control over which input cases are considered known issues.
  • An xpassed result indicates that a test marked for failure has started passing, which often requires developer attention.
  • The reason argument is best practice for documenting why a specific test is marked as an expected failure.
  • Overusing xfail can hide real regressions, so always pair it with clear documentation and intended follow-up actions.

Common mistakes

  • Mistake: Using xfail to suppress actual bugs that should be fixed. Why it's wrong: xfail is meant for tests that are known to fail but are not yet prioritized to be fixed. Using it on active regressions hides technical debt. Fix: Use xfail only for temporary cases and track them in issue tracking systems.
  • Mistake: Misinterpreting the 'strict' parameter. Why it's wrong: Setting strict=True causes an xpassed test to be reported as a failure rather than an expected pass. Developers often forget this and are surprised when their test suite fails. Fix: Ensure that you understand whether you want xpassed tests to trigger a failure or just a warning.
  • Mistake: Applying xfail to a test that hangs or crashes. Why it's wrong: pytest might not be able to finish executing the test to reach the point where it records the failure, potentially causing the entire test runner to hang. Fix: Use mark.skip for tests that break the environment or cause instability.
  • Mistake: Adding a reason string to xfail but never updating it. Why it's wrong: It leads to confusion for other developers who might believe the reason is still valid when it is not. Fix: Periodically review tests marked with xfail and update or remove the reason string if it becomes obsolete.
  • Mistake: Overusing xfail instead of parameterizing test cases. Why it's wrong: If only one scenario fails in a set, marking the whole test function as xfail makes all other scenarios (that might be passing) invisible in the pass/fail report. Fix: Parameterize the test and only mark the specific failing parameter combination as xfail.

Interview questions

What is the purpose of the 'xfail' marker in pytest and when should it be used?

The 'xfail' marker is used to indicate that a test is expected to fail. You use it when you have a known issue in your application—such as a feature that is not yet implemented or a bug that you are still actively debugging—but you do not want the test suite to report an error or exit with a non-zero status. By marking it as 'xfail', you acknowledge the failure explicitly, allowing the test suite to remain green overall while still documenting the specific behavior that currently falls short of expectations.

How does pytest handle an 'xfail' test if it unexpectedly passes?

If a test marked with '@pytest.mark.xfail' actually passes, pytest reports it as an 'xpass' (unexpected pass). This is a critical feature because it alerts the developer that the code has changed and the previously failing scenario now works. You can configure this behavior using the 'strict=True' parameter in the marker. When 'strict' is enabled, an unexpected pass will cause the test to be treated as a failure, forcing the developer to remove the 'xfail' marker because the underlying issue has been resolved.

How can you use the 'reason' parameter with 'xfail', and why is it important for team collaboration?

The 'reason' parameter allows you to pass a string explaining why the test is expected to fail, such as '@pytest.mark.xfail(reason="Waiting for API endpoint to be fixed").' This is essential in team settings because it provides immediate context to other developers. Without a reason, a teammate seeing an 'xfail' might assume the test is broken or irrelevant. A descriptive reason serves as documentation, explaining the history of the issue and indicating when the test might be expected to pass again.

What is the difference between using '@pytest.mark.xfail' and using 'pytest.xfail()' inside the test body?

The primary difference lies in the execution flow. When you use the decorator '@pytest.mark.xfail', pytest skips the test entirely if it is marked, or runs it and reports the result accordingly. Conversely, calling 'pytest.xfail()' inside the test body causes the test to stop execution immediately at that line and mark the result as an xfail. You would use the function call approach when you need to dynamically decide to xfail a test based on logic that occurs during the test's execution, such as checking a specific environment variable or a complex runtime condition.

Compare the use of 'xfail' versus 'skip' in pytest. How do you decide which one is appropriate?

The decision depends on whether the test is expected to pass or fail. 'Skip' is used when the test cannot be run at all, perhaps due to missing dependencies or an incompatible operating system, meaning the test is currently irrelevant. 'Xfail', however, is used when the test can be run but you anticipate it will produce a failure. You choose 'skip' when you want to avoid execution overhead or errors related to environment issues, and you choose 'xfail' when you want to keep tracking the status of a known, non-fatal bug in the code.

How can you conditionally apply 'xfail' based on external factors like the operating system or the Python version?

You apply conditional xfails using the 'condition' argument within the marker, such as '@pytest.mark.xfail(sys.platform == "win32", reason="Known issue on Windows").' This is a powerful feature for cross-platform testing because it allows your test suite to account for platform-specific quirks without failing the entire build. By evaluating a boolean expression inside the decorator, pytest automatically decides whether to apply the xfail logic. This ensures that your continuous integration remains stable across different environments, preventing known platform-specific issues from blocking the deployment pipeline while still providing visibility into where those specific failures exist.

All pytest & Testing interview questions →

Check yourself

1. When a test is marked with @pytest.mark.xfail and it unexpectedly passes (xpass), what is the default behavior in pytest?

  • A.The test is reported as a failure
  • B.The test is reported as an expected pass (xpass)
  • C.The test is ignored entirely
  • D.The test results in a SystemExit
Show answer

B. The test is reported as an expected pass (xpass)
By default, pytest reports an xpass as a special 'xpass' outcome. Option 0 is incorrect because it only happens if strict=True is set. Option 2 is incorrect because the test still executes. Option 3 is irrelevant to the reporting mechanism.

2. Which of the following scenarios is the most appropriate use case for pytest.mark.xfail?

  • A.A feature is not yet implemented, so the test fails
  • B.A platform-specific bug exists that cannot be fixed until a third-party dependency is updated
  • C.The test takes too long to run on the CI server
  • D.The developer wants to prevent the test from running at all
Show answer

B. A platform-specific bug exists that cannot be fixed until a third-party dependency is updated
xfail is for tests that are known to fail due to external factors or deferred fixes. Option 0 is better handled by a 'skip' mark. Option 2 is a performance issue, not an expectation issue. Option 3 is explicitly solved by 'skip'.

3. What happens if a test is marked @pytest.mark.xfail(strict=True) and the test passes?

  • A.The test suite continues as if nothing happened
  • B.The test is reported as an xpass
  • C.The test is reported as a failure
  • D.The test is automatically skipped
Show answer

C. The test is reported as a failure
When strict=True, an unexpected pass is treated as a test failure. This is used to ensure developers clean up tests that no longer need the xfail status. The other options describe default behaviors, not strict mode behavior.

4. If you want to only mark a test as xfail if a specific condition is met (e.g., based on the Python version), how should you implement this?

  • A.Use an if-statement inside the test body to call pytest.xfail()
  • B.Pass a 'condition' argument to @pytest.mark.xfail
  • C.Use @pytest.mark.skipif instead
  • D.There is no way to conditionally mark a test as xfail
Show answer

B. Pass a 'condition' argument to @pytest.mark.xfail
The 'condition' argument (or boolean expression) inside @pytest.mark.xfail allows for dynamic marking. Option 0 is possible but less declarative than the decorator. Option 2 skips the test, which is a different outcome. Option 3 is false because the decorator is designed for this.

5. What is the primary difference between @pytest.mark.skip and @pytest.mark.xfail?

  • A.xfail runs the test, skip does not
  • B.skip runs the test, xfail does not
  • C.xfail always passes, skip always fails
  • D.There is no functional difference
Show answer

A. xfail runs the test, skip does not
xfail executes the code to check if it still fails (or if it has started passing), whereas skip stops the test runner from executing the code entirely. Options 1, 2, and 3 misrepresent the execution flow and the outcomes of these two markers.

Take the full pytest & Testing quiz →

← PreviousSkipping Tests — skip and skipifNext →unittest.mock — MagicMock and patch

pytest & Testing

25 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app