From 654e09abcc3bd7ca1320a177142127da22868be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Maj?= Date: Sat, 26 Sep 2026 12:37:10 +0200 Subject: [PATCH] test: add whitespace edge cases --- docs/04_pytest.md | 47 ++++++++++++++++++++++++++++++++++------ docs/ROADMAP.md | 2 ++ tests/test_text_stats.py | 46 ++++++++++++++++++++------------------- 3 files changed, 66 insertions(+), 29 deletions(-) diff --git a/docs/04_pytest.md b/docs/04_pytest.md index d0cfdd6..54b9bda 100644 --- a/docs/04_pytest.md +++ b/docs/04_pytest.md @@ -79,10 +79,10 @@ Inside those files, test functions usually start with `test_`. Example: ```python -def test_count_words_returns_zero_for_empty_text() -> None: - result = count_words("") +def test_count_characters_includes_whitespace_by_default() -> None: + result = count_characters("hello world") - assert result == 0 + assert result == 11 ``` The function name describes the expected behavior. @@ -108,15 +108,48 @@ This means: Example: ```python -def test_normalize_whitespace_replaces_repeated_spaces() -> None: - text = "Python is fun" +def test_count_characters_can_ignore_whitespace() -> None: + result = count_characters("hello world", include_whitespace=False) + assert result == 10 +``` + +This style makes tests easier to read and review. + +## Parametrizing related cases + +Sometimes several inputs should follow the same behavior. Copying the entire +test for each input would make the test file repetitive. + +pytest can run one test function with several sets of values: + +```python +import pytest + + +@pytest.mark.parametrize( + ("text", "expected"), + [ + ("Python is fun", "Python is fun"), + ("Python\tis\nfun", "Python is fun"), + (" \t\n ", ""), + (" Python project ", "Python project"), + ], +) +def test_normalize_whitespace_handles_common_inputs(text: str, expected: str) -> None: result = normalize_whitespace(text) - assert result == "Python is fun" + assert result == expected ``` -This style makes tests easier to read and review. +The names `text` and `expected` match the two values in every tuple. pytest +runs the function once for each tuple and reports every case separately. + +This example covers repeated spaces, tabs and newlines, whitespace-only text, +and leading or trailing spaces without duplicating the test logic. + +Parametrization is useful when the action and assertion stay the same. Use +separate tests when cases describe different behavior or need different setup. ## pytest configuration diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index a5f99b8..2abd666 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -83,6 +83,8 @@ These parts are already included in the repository. - [x] Explain linting and formatting - [x] Explain when ignoring linting rules may be reasonable - [x] Check internal Markdown links with pytest +- [x] Add whitespace edge cases to the example test suite +- [x] Demonstrate pytest parametrization with related inputs ### Continuous integration diff --git a/tests/test_text_stats.py b/tests/test_text_stats.py index f7dc267..abf475c 100644 --- a/tests/test_text_stats.py +++ b/tests/test_text_stats.py @@ -1,5 +1,7 @@ """Tests for basic text statistics utilities.""" +import pytest + from text_toolkit import ( count_characters, count_words, @@ -7,34 +9,34 @@ ) -def test_normalize_whitespace_replaces_repeated_spaces() -> None: - text = "Python is fun" - - result = normalize_whitespace(text) - - assert result == "Python is fun" - - -def test_normalize_whitespace_handles_tabs_and_newlines() -> None: - text = "Python\tis\nfun" - +@pytest.mark.parametrize( + ("text", "expected"), + [ + ("Python is fun", "Python is fun"), + ("Python\tis\nfun", "Python is fun"), + (" \t\n ", ""), + (" Python project ", "Python project"), + ], +) +def test_normalize_whitespace_handles_common_inputs(text: str, expected: str) -> None: result = normalize_whitespace(text) - assert result == "Python is fun" - - -def test_count_words_returns_zero_for_empty_text() -> None: - result = count_words("") + assert result == expected - assert result == 0 - - -def test_count_words_handles_repeated_whitespace() -> None: - text = "Python project\nworkflow" +@pytest.mark.parametrize( + ("text", "expected"), + [ + ("", 0), + (" \t\n ", 0), + (" Python project ", 2), + ("Python project\nworkflow", 3), + ], +) +def test_count_words_handles_common_whitespace(text: str, expected: int) -> None: result = count_words(text) - assert result == 3 + assert result == expected def test_count_characters_includes_whitespace_by_default() -> None: