Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added dedicated page about using types with pytest #12842 #12963

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ lovetheguitar
Lukas Bednar
Luke Murphy
Maciek Fijalkowski
Maggie Chung
Maho
Maik Figura
Mandeep Bhutani
Expand Down
2 changes: 2 additions & 0 deletions changelog/12842.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Added dedicated page about using types with pytest
See :ref:`types` for detailed usage.
142 changes: 142 additions & 0 deletions doc/en/how-to/types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
.. _types:

How to use Types with Pytest
===========================================

You can add typing in pytest functions that helps to specify what type of data a function expects and returns.
This guide explains how to apply typing in pytest to improve code readability and prevent type-related errors.


1. Introduction to Typing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would replace this entire section with a paragraph like "this page assumes the reader already knows how typing in Python works, its benefits, etc, for more information see PAGE".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a section like "Why type tests", explaining the motivation to add types to tests (which are slightly different than the reasons to add typing to production code).

-----------------

Typing in Python helps developers specify the expected data types of variables, function parameters, and return values.
It improves code clarity and prevents type errors.

For example:

.. code-block:: python

def add(x: int, y: int) -> int:
return x + y

In this example:
`x: int` and `y: int` mean that `x` and `y` should be integers.
`-> int` shows that the function returns an integer.

It makes the intentions clear that both the input and output are expected to be integers.

2. Typing Test Functions
-----------------
Comment on lines +29 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sections need to use the same number of characters as the title, here for example:

Suggested change
2. Typing Test Functions
-----------------
2. Typing Test Functions
------------------------

Test functions in pytest check whether the code runs correctly.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned, the reason for a test is different: it is better to run the test in order to see if the code is correct -- but we add typing to help with refactoring and readability. 👍

While test functions do not return values, we can add `-> None` as the return type.

For example:

.. code-block:: python

import pytest


def test_add() -> None:
result = add(2, 3)
assert result == 5

In this function:
`test_add` is typed as `-> None` because it does not return anything.
Comment on lines +45 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can instead mention that while at first marking a function as returning -> None might seem useless, having at least a type annotation means most type checkers will now type check the function, so it is useful because of that.

The assertion `assert result == 5` checks if the result is correct.

Example result:
If the input data type is incorrect, like passing `add("2", 3)`, a `TypeError` will occur.
Comment on lines +45 to +50
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are not needed, as we assume readers already know about Python typing.


3. Typing Fixtures
-----------------
Fixtures in pytest helps set up data or provides resources needed for tests.
Adding type hints helps clarify what kind of data the fixtures returns, making code easier to read and easier to debug.

* Basic Fixture Typing:

If a fixture returns a number, you can specify it returns an `int`:

.. code-block:: python

import pytest


@pytest.fixture
def sample_fixture() -> int:
return 38


def test_sample_fixture(sample_fixture: int) -> None:
assert sample_fixture == 38

In this example:
`sample_fixture()` is typed to return an `int`.
In `test_sample_fixture`, using typing with fixtures, it ensures the return is an integer.
If you change the return from an integer to a string (``"42"``), the test will fail.


* Typing Fixtures with Lists and Dictionaries:
Here are the examples showing how to use List and Dict types in pytest.
When you want to use complex data structures like lists or dictionaries, import `List` and `Dict` from Python's `typing` module to specify the types.
Note: From Python 3.5 or later, typing module is built-in module in Python.
Comment on lines +80 to +83
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can drop this too, again we can assume the reader knows about typing and the particulars about list/List, etc.



.. code-block:: python

from typing import List
import pytest


# Fixture returning a list of integers
@pytest.fixture
def sample_list() -> List[int]:
return [5, 10, 15]


def test_sample_list(sample_list: List[int]) -> None:
assert sum(sample_list) == 30
In this example, `List[int]` ensures that the list contains only integers, allowing functions like sum() to operate.


.. code-block:: python

from typing import Dict
import pytest


# Fixture returning a dictionary
@pytest.fixture
def sample_dict() -> Dict[str, int]:
return {"a": 50, "b": 100}


def test_sample_dict(sample_dict: Dict[str, int]) -> None:
assert sample_dict["a"] == 50
In this example, `Dict[str, int]` ensures that each key is a string and each value is an integer, ensuring clarity and consistency when accessing dictionary elements by key.


4. Typing Parameterized Tests
----------
`@pytest.mark.parametrize` allows developer to run the test multiple times with different sets of arguments.
Adding types helps to maintain consistency of values, especially for complex inputs.

For example, you are testing if adding 1 to `input_value` results in `expected_output` for each set of arguments.

.. code-block:: python

import pytest


@pytest.mark.parametrize("input_value, expected_output", [(1, 2), (5, 6), (100, 101)])
def test_increment(input_value: int, expected_output: int) -> None:
assert input_value + 1 == expected_output

In this example:
You are expecting the type of both `input_value` and `expected_output` are integers.


Conclusion
----------
Using typing in pytest helps improve readability of the tests and fixture returns, and prevent errors.
Loading