Pythonパッケージのテストタイプ#
Three types of tests: unit, integration, and functional tests#
テストスイートを作成する際に考慮したいテストには、さまざまなタイプがあります:
単体テスト
統合テスト
End-to-end (also known as functional) tests
Each type of test has a different purpose. Here, you will learn about all three types of tests by working through simple examples.
単体テスト#
単体テストは、個々のコンポーネントやコードの単位を分離してテストし、それらが正しく動作することを確認するものです。単体テストの目的は、ソフトウェアの各部分、通常は関数やメソッドのレベルが、意図したタスクを正しく実行することを検証することです。
Unit tests can be compared to examining each piece of your puzzle to ensure parts or subsections of it are not broken. If all of the pieces of that section of your puzzle don't fit together, you will never complete it. Similarly, when working with code, tests ensure that each function, attribute, class, and method works properly when isolated.
Unit test example: Suppose you have a function that adds two numbers together. A unit test for that function ensures that when provided with two numbers, it returns the correct sum. This is a unit test because it checks a single unit (function) in isolation.
# src/examplePy/numbers.py
def add_numbers(a: float, b: float) -> float:
"""
Add two numbers together and return the result.
Parameters
----------
a : float
The first number to add.
b : float
The second number to add.
Returns
-------
float
The sum of the two numbers.
"""
return a + b
上記の関数のユニットテスト例。このテストは、 tests/ ディレクトリの pytest コマンドを使って実行します。
# tests/examplePy/test_numbers.py
from examplePy.numbers import add_numbers
def test_add_numbers():
"""Test the add_numbers function."""
# test with positive numbers
result = add_numbers(2, 3)
assert result == 5, f"Expected 5, but got {result}"
# test with negative numbers
result2 = add_numbers(-1, 4)
assert result2 == 3, f"Expected 3, but got {result2}"
# test with zero
result3 = add_numbers(0, 5)
assert result3 == 5, f"Expected 5, but got {result3}"
上記のテストは、数値を加算する 1 つのケースだけをテストするわけではないことに注意してください。代わりに、正の数の追加、負の数の追加、ゼロの追加などの複数のシナリオをテストします。これは、「add_numbers」関数がさまざまな状況で正しく動作することを保証するのに役立ち、防御的なプログラミングについて考える始まりになります。
pytest testing/test_math_utils.py を使用してターミナルからこのテストを実行できます。
ユニットテストでは、コードの各部分がそれ自体で期待通りに動作することを確認する必要があります。#
統合テスト#
統合テストでは、パッケージの各パーツがどのように連動するか、あるいは統合されるかをテストします。統合テストは、パズルのピースをつなげて全体像を作るようなものです。統合テストは、コードのさまざまな部分がどのように適合し、連携して動作するかに焦点を当てます。
たとえば、温度を変換し、統計を計算する関数があるとします。統合テストでは、温度を変換して分析するワークフロー内でこれらの機能が正しく連携して動作することを確認します。
# src/examplePy/temperature.py
def celsius_to_fahrenheit(celsius: float) -> float:
"""
Convert temperature from Celsius to Fahrenheit.
Parameters:
celsius (float): Temperature in Celsius.
Returns:
float: Temperature in Fahrenheit.
"""
fahrenheit = (celsius * 9 / 5) + 32
return fahrenheit
def fahrenheit_to_celsius(fahrenheit: float) -> float:
"""
Convert temperature from Fahrenheit to Celsius.
Parameters:
fahrenheit (float): Temperature in Fahrenheit.
Returns:
float: Temperature in Celsius.
"""
celsius = (fahrenheit - 32) * 5 / 9
return celsius
def average_temperature(temps: list[float]) -> float:
"""
Calculate average temperature from a list.
Parameters
----------
temps : list
List of temperatures.
Returns
-------
float
Average temperature.
"""
return sum(temps) / len(temps)
def convert_and_average(temps_celsius: list[float]) -> float:
"""
Convert list of Celsius temps to Fahrenheit and
calculate the average.
Parameters
----------
temps_celsius : list
List of Celsius temperatures.
Returns
-------
float
Average temperature in Fahrenheit.
"""
temps_fahrenheit = [celsius_to_fahrenheit(t)
for t in temps_celsius]
return average_temperature(temps_fahrenheit)
以下は、変換関数と統計関数がどのように連携するかをチェックする統合テストです。
# tests/examplePy/test_temperature.py
from examplePy import temperature
def test_convert_and_average():
"""
Test that convert_and_average correctly combines conversion
and averaging.
"""
# Test with known values: [0, 10, 20] Celsius
# Should average to 10 Celsius = 50 Fahrenheit
temps_celsius = [0, 10, 20]
result = temperature.convert_and_average(temps_celsius)
assert abs(result - 50.0) < 0.01
# Test with different values
temps_celsius = [0, 100]
result = temperature.convert_and_average(temps_celsius)
# Average of 32 and 212 Fahrenheit = 122
assert abs(result - 122.0) < 0.01
この統合テストでは、変換関数と平均化関数が実際のワークフローで期待どおりに連携して動作することを検証します。
パズルのピースの両端が欠けていると、パズルの他の要素と連動することができません。ソフトウェアの個々の関数、メソッド、クラスについても同様です。コードは、ある一連のタスクを実行するために、個々に、そして一緒に働く必要があります。#
統合テストは、コードの一部が連携して動作することが期待されており、期待通りに動作することを保証するものでなければなりません。#
エンドツーエンド(機能)テスト#
End-to-end tests (also referred to as functional tests) in Python are like comprehensive checklists for your software. They simulate real user workflows to make sure the code base supports real-life applications and use-cases from start to finish. These tests help catch issues that might not show up in smaller tests and ensure your entire application behaves correctly. Think of them as a way to give your software a final check before it's put into action, making sure it's ready to deliver a smooth user experience.
End-to-end or functional tests represent an entire workflow that your package supports.#
エンドツーエンドのテスト例: パッケージを開いて温度データを摂氏から華氏に処理/変換し、平均温度を計算するとします。エンドツーエンドのテストでは、このワークフロー全体をシミュレートし、パッケージが入力温度データを正しく処理し、要約平均値を返すことを確認します。エンドツーエンドのテストでは、サンプル データを提供し、ワークフロー全体を実行して、最終出力が正しいことを確認します。
# tests/examplePy/test_temperature.py
def test_temperature_workflow():
"""
Test the complete temperature processing workflow.
This end-to-end test provides sample temperature data in
Celsius, processes it through the full workflow
(conversion and averaging), and verifies the output is
correct.
"""
# Sample temperature data in Celsius
temps_celsius = [0, 10, 20]
# Run the complete workflow
result = temperature.convert_and_average(temps_celsius)
# Verify the output
# Average of 32, 50, and 68 Fahrenheit = 50 Fahrenheit
assert abs(result - 50.0) < 0.01
このエンドツーエンドのテストでは、サンプル データの提供、変換と平均化、出力が正しいことの確認というユーザー ワークフロー全体を実行します。
End-to-end tests also verify how a program runs from start to finish.
A tutorial that you add to your documentation and run in CI is another
example of an end-to-end test. For example, a Jupyter (.ipynb) notebook or
.md file with embedded code that demonstrates a complete user
workflow.
注釈
For scientific packages, creating short tutorials that highlight core workflows that your package supports, that are run when your documentation is built, could also serve as end-to-end tests.
どのテストタイプをいつ使用するか#
テストが初めての場合は、単体テストから始めてください。これらは最も簡単に作成でき、最も速く実行でき、デバッグも簡単です。パッケージが成長するにつれて、最も価値が高まる統合テストとエンドツーエンドのテストを追加できます。
単体テストを作成することから始めます#
単一の関数、メソッド、またはクラスを分離してテストしていますか?
→ はい: 単体テスト を作成します。
例: add_numbers(2, 3) が 5 を返すことを確認します。
単体テストはコードの他の部分に依存しません
これらのテストはテスト スイートの基礎を形成します
何かが壊れた場合、単体テストによりどこが壊れているかを簡単に見つけることができます。
Add integration tests next#
複数のコンポーネントがどのように連携して動作するかをテストしていますか?
→ はい: 統合テスト と書き込みます。
例: 温度を変換し、その平均を計算する
統合テストは、個々の部分がすでに機能していることを前提としています
これらのテストは、コンポーネントが正しく相互作用することを検証します。
コアワークフローにエンドツーエンドテストを使用する#
完全で現実的なユーザー ワークフローを最初から最後までテストしていますか?
→ はい: エンドツーエンド テスト を使用します。
例: ユーザーが従うであろう完全なデータ処理ワークフローを実行する
これらのテストは多くの場合、ドキュメント内の例を反映しています。
最も重要なワークフローには慎重に使用してください。
ドキュメントのビルド中に実行されるチュートリアルは、エンドツーエンドのテストとして機能します。
Comparing unit, integration, and end-to-end tests#
Unit tests, integration tests, and end-to-end tests have complementary advantages and disadvantages. The fine-grained nature of unit tests makes them well-suited for isolating where errors are occurring. However, unit tests are not useful for verifying that different sections of code work together.
統合テストとエンドツーエンド テストは、プログラムのさまざまな部分が連携して動作することを検証しますが、エラーが発生している場所を正確に即座に特定することにはあまり価値がありません。
テストは完璧である必要はない#
It is important to note that you don't need to spend energy worrying about the specifics of test types. When you begin to work on your test suite, consider what your package does and how you may need to test parts of it. Being familiar with different test types provides a framework to help you think about writing tests and how they can complement each other.
Next steps#
テストの種類を理解したので、パッケージの 効果的なテストを作成する 方法を学びましょう。次に、ローカルでテストを実行する および 継続的インテグレーション で行う方法を調べます。 CodeCov などのツールを使用してテスト カバレッジの追跡について学ぶこともできます。