Supprimer Rendre public Rendre privé Add tags Delete tags
  Ajouter un tag   Annuler
  Supprimer le tag   Annuler
  • • DevOps notes •
  •  
  • Tags
  • Connexion

Automated Testing with Pytest/shaare/HoeITw

  • python
  • python

Assertions in Pytest

  • Pytest uses Python’s built-in assert statement to declare expected conditions in tests, making test code concise and readable.
  • When an assert expression evaluates to True, execution continues; if it evaluates to False, an AssertionError is raised and Pytest marks the test as failed.
  • Pytest intercepts assertion failures to provide detailed, introspective feedback on why an assertion failed.

The assert Statement

  • The assert keyword checks that an expression is truthy; if it’s falsy, Python raises AssertionError.
  • You can append an optional message: assert expression, "message", which will be shown if the assertion fails.
  • In plain Python, assert x == 5 does nothing when true, while assert x == 10, "x should be 10" raises an error with that message if the condition is false.

Pytest and assert

  • Pytest enhances the built-in assert by inspecting the expression’s values and rewriting the failure message to show variable states.
  • Common assertion patterns include:
    • Equality and inequality checks to compare expected versus actual values.
    • Truthiness or falsiness checks to verify that objects are non-empty or evaluate to False.
    • Membership checks using in or not in to assert presence or absence in containers.
    • Comparison operators (<, >, <=, >=) to verify ordering conditions.

Pytest’s Rich Failure Output

  • When an assertion fails, Pytest displays the values from the expression and highlights exactly where they differ.

Asserting Floating-Point Numbers (pytest.approx)

  • Floating-point arithmetic can yield tiny precision errors, so direct equality comparisons may fail unexpectedly.
  • Pytest provides pytest.approx to compare floats within a tolerance, supporting both relative and absolute tolerances.

Asserting Exceptions (pytest.raises)

  • Use with pytest.raises(ExpectedException): as a context manager to assert that a block of code raises a specific exception.
  • You can include match="regex" to verify that the exception message matches a given pattern.
  • This allows testing both that the correct error type is raised and that its message contains expected details.

Common Pitfalls & How to Avoid Them

  • Avoid overly complex expressions in a single assert; break them into multiple simpler assertions for clarity.
  • Always use pytest.approx for floating-point comparisons to prevent false negatives from tiny precision differences.
from text_analysis import calculate_text_attributes
import pytest

# Section: The `assert` Statement

# Uncomment to play around with Python assertions

# x: int = 5

# assert x == 5  # Nothing will happen, because this is True
# assert (
#     x == 10
# ), "x should be 10, but it's not!"  # Raise an AssertionError

# Section: Pytest and `assert`

def test_string_equality() -> None:
    expected_status = "SUCCESS"
    actual_status = "success".upper()

    assert actual_status == expected_status

def test_word_count() -> None:
    text = "Deploying microservice to Kubernetes cluster."
    text_empty = ""

    assert (calculate_text_attributes(text)["word_count"]) == 5
    assert (
        calculate_text_attributes(text_empty)["word_count"]
    ) == 0

def test_unique_words() -> None:
    text = "Deploying microservice to Kubernetes cluster."
    text_with_duplicates = "Deploying deploying."
    text_empty = ""

    text_results = calculate_text_attributes(text)
    text_with_duplicates_result = calculate_text_attributes(
        text_with_duplicates
    )
    text_empty_results = calculate_text_attributes(text_empty)

    assert (len(text_results["unique_words"])) == 5
    assert (
        len(text_with_duplicates_result["unique_words"])
    ) == 1
    assert (len(text_empty_results["unique_words"])) == 0

def test_average_word_length() -> None:
    text = "Deploying microservice to Kubernetes cluster."  # 40 / 5 = 8
    text_with_duplicates = "Deploying deploying."  # 18 / 2 = 9
    text_empty = ""  # 0

    text_results = calculate_text_attributes(text)
    text_with_duplicates_result = calculate_text_attributes(
        text_with_duplicates
    )
    text_empty_results = calculate_text_attributes(text_empty)

    assert (text_results["average_word_length"]) == 8.0
    assert (
        text_with_duplicates_result["average_word_length"]
    ) == 9.0
    assert (text_empty_results["average_word_length"]) == 0.0

def test_longest_word() -> None:
    text = "Deploying microservice to Kubernetes cluster."  # microservice
    text_with_duplicates = "Deploying deploying."  # deploying
    text_empty = ""

    text_results = calculate_text_attributes(text)
    text_with_duplicates_result = calculate_text_attributes(
        text_with_duplicates
    )
    text_empty_results = calculate_text_attributes(text_empty)

    assert (
        text_results["longest_word"].lower()
    ) == "microservice"
    assert (
        text_with_duplicates_result["longest_word"].lower()
    ) == "deploying"
    assert (text_empty_results["longest_word"]) == ""

# Section: Pytest’s Rich Failure Output

@pytest.mark.xfail  # We're marking the test as an expected failure
def test_string_mismatch() -> None:
    expected = "HEllo WOrlD"
    actual = "hello world"

    assert expected == actual

# Section: Asserting Floating-Point Numbers (`pytest.approx`)

def test_float_with_approx() -> None:
    calculated_val = 0.1 + 0.2
    expected_val = 0.3

    assert calculated_val == pytest.approx(expected_val)  # type: ignore

# Section: Asserting Exceptions (`pytest.raises`)

def test_raises_exception() -> None:
    with pytest.raises(ZeroDivisionError):
        _division = 1 / 0
from typing import TypedDict
import re

class TextAttributes(TypedDict):
    word_count: int
    unique_words: set[str]
    average_word_length: float
    longest_word: str

def calculate_text_attributes(input_text: str) -> TextAttributes:
    split_text = re.findall(r"\w+", input_text)
    word_length_sum = sum(len(word) for word in split_text)
    avg_word_length = (
        word_length_sum / len(split_text)
        if len(split_text)
        else 0
    )

    return {
        "word_count": len(split_text),
        "unique_words": set(text.lower() for text in split_text),
        "average_word_length": avg_word_length,
        "longest_word": (
            max(split_text, key=len) if split_text else ""
        ),
    }
2 months ago Permalien
cluster icon
  • Adding Tests to a Multi-File Project : Adding Tests to a Multi-File Project Standard Project Layout with Tests To maintain a clean and organized codebase, it is standard practice to separat...
  • Mocking : Mocking Fundamentals Introduction When unit testing DevOps scripts that interact with external systems, tests can become slow, unreliable, difficult ...
  • Making HTTP Requests : Making HTTP Requests The requests library simplifies HTTP interactions by abstracting raw HTTP details, making it ideal for DevOps automation tasks. ...
  • Log Levels in Practice : Log Levels in Practice Python defines five standard levels with increasing severity: DEBUG (10): Detailed diagnostic information. INFO (20): Confirm...
  • Structured Logging : Introduction to Structured Logging Plain-text logs are hard to parse and brittle to format changes. Structured logging records events as key-value da...


(110)
Filtrer par liens sans tag
Replier Replier tout Déplier Déplier tout Êtes-vous sûr de vouloir supprimer ce lien ? Êtes-vous sûr de vouloir supprimer ce tag ? Le gestionnaire de marque-pages personnel, minimaliste, et sans base de données par la communauté Shaarli