Setiap programmer pasti pernah mengalami momen ketika kode yang "sudah beres" tiba-tiba rusak setelah diubah sedikit. Tanpa pengujian otomatis, kamu harus mengecek semuanya secara manual — lambat dan rawan terlewat. Di sinilah Pytest berperan: framework testing paling populer di Python yang membuat proses menulis dan menjalankan test menjadi sangat mudah. Di artikel ini kamu akan belajar Pytest dari nol hingga mampu menulis test yang rapi, otomatis, dan percaya diri untuk mengubah kode.
Mengapa Harus Menulis Test?
Menulis test mungkin terasa seperti pekerjaan tambahan, tetapi manfaatnya sangat besar. Test otomatis berfungsi sebagai jaring pengaman: setiap kali kamu mengubah kode, kamu bisa langsung tahu apakah ada bagian lain yang ikut rusak (regresi). Ini membuat proses refactoring menjadi jauh lebih aman dan memudahkan kolaborasi dalam tim. Pytest menjadi pilihan utama karena sintaksnya sederhana — cukup gunakan fungsi biasa dan kata kunci assert bawaan Python, tanpa perlu class atau boilerplate yang rumit.
Instalasi Pytest
Sebelum mulai, pastikan Python sudah terpasang di komputermu. Kalau belum, kamu bisa ikuti panduan cara install Python dan menulis program pertamamu. Setelah itu, sebaiknya buat virtual environment terlebih dahulu, lalu install Pytest lewat pip:
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install pytest
Selalu gunakan virtual environment agar dependensi proyek tidak tercampur dengan paket global di sistem. Setelah diaktifkan, prompt terminalmu biasanya diawali dengan (.venv).
Menulis Test Pertamamu
Buat file baru bernama calculator.py berisi fungsi sederhana yang akan kita uji:
# calculator.py
def add(a, b):
return a + b
def divide(a, b):
if b == 0:
raise ValueError("tidak bisa membagi dengan nol")
return a / b
Kemudian buat file test dengan nama test_calculator.py. Aturan penamaannya penting: Pytest secara otomatis menemukan file yang diawali test_ atau diakhiri _test. Di dalamnya, tulis fungsi yang juga diawali test_:
# test_calculator.py
from calculator import add, divide
def test_add_dua_bilangan_positif():
assert add(2, 3) == 5
def test_add_bilangan_negatif():
assert add(-1, -1) == -2
def test_divide_normal():
assert divide(10, 2) == 5.0
Jalankan test dengan perintah pytest di terminal. Kamu akan melihat output berwarna hijau berisi jumlah test yang lolos. Itu saja! Tidak perlu class, tidak perlu boilerplate — Pytest menemukan dan menjalankan fungsi test-mu secara otomatis.
pytest
Memahami Fixtures: Menghindari Kode Berulang
Sering kali beberapa test butuh data atau objek yang sama untuk disiapkan (misalnya koneksi database atau objek konfigurasi). Daripada mengulang kode di setiap test, gunakan fixture dengan dekorator @pytest.fixture.
import pytest
@pytest.fixture
def data_bilangan():
"""Menyediakan data awal untuk test."""
return {"a": 10, "b": 20}
def test_add_dengan_fixture(data_bilangan):
assert add(data_bilangan["a"], data_bilangan["b"]) == 30
def test_divide_dengan_fixture(data_bilangan):
assert divide(data_bilangan["a"], data_bilangan["b"]) == 0.5
Fixture cukup ditulis sebagai nama parameter fungsi test. Pytest akan otomatis "menyuntikkan" nilai kembalian fixture ke parameter tersebut — konsep ini disebut dependency injection. Kamu juga bisa menulis fixture dengan yield untuk menjalankan kode pembersihan setelah test selesai.
Parametrize: Satu Test, Banyak Kasus
Jika kamu ingin menguji fungsi yang sama dengan banyak kombinasi input, gunakan @pytest.mark.parametrize. Ini mencegah duplikasi kode sekaligus memperjelas daftar kasus uji.
import pytest
from calculator import add
@pytest.mark.parametrize(
"a, b, hasil",
[
(1, 2, 3),
(-1, 1, 0),
(100, 200, 300),
(0, 0, 0),
],
)
def test_add_berbagai_kasus(a, b, hasil):
assert add(a, b) == hasil
Setiap tuple dalam daftar akan menjadi satu test terpisah. Dengan empat tuple di atas, Pytest akan menjalankan empat test dan menampilkannya satu per satu di laporan.
Menguji Exception yang Diangkat
Kode yang baik akan melempar exception untuk kondisi tidak valid. Kamu bisa menguji hal ini dengan pytest.raises:
import pytest
from calculator import divide
def test_divide_dengan_nol_melempar_error():
with pytest.raises(ValueError, match="tidak bisa membagi"):
divide(10, 0)
Blok with pytest.raises(...) akan lolos jika exception yang diharapkan diangkat, dan gagal jika ternyata tidak ada exception (atau tipenya berbeda). Parameter match berguna untuk memastikan pesan errornya sesuai.
Mocking: Mengisolasi Ketergantungan Eksternal
Dalam aplikasi nyata, fungsi sering bergantung pada layanan eksternal seperti API, database, atau waktu. Test yang bagus harus terisolasi — tidak boleh gagal hanya karena internet mati. Di sinilah mocking berperan: mengganti ketergantungan asli dengan versi tiruan.
import requests
from unittest.mock import patch
def ambil_data_github(username):
resp = requests.get(f"https://api.github.com/users/{username}")
return resp.json()["name"]
@patch("requests.get")
def test_ambil_data_github(mock_get):
mock_get.return_value.json.return_value = {"name": "Octocat"}
assert ambil_data_github("octocat") == "Octocat"
mock_get.assert_called_once()
Gunakan mocking secukupnya. Jika hampir semua hal di-mock, test-mu mungkin tidak lagi menguji perilaku nyata. Fokuslah pada isolasi ketergantungan eksternal yang lambat atau tidak bisa diandalkan.
Flag Berguna Saat Menjalankan Pytest
Pytest punya banyak opsi command-line yang mempermudah debugging. Beberapa yang paling sering dipakai:
pytest -v # verbose: tampilkan nama setiap test
pytest -k "divide" # jalankan hanya test yang namanya mengandung "divide"
pytest -x # berhenti pada kegagalan pertama
pytest --maxfail=2 # berhenti setelah 2 kegagalan
pytest --pdb # masuk debugger saat test gagal
pytest --cov=calculator # (butuh plugin pytest-cov) laporan cakupan kode
Menguji API dengan Pytest dan FastAPI
Pytest juga menjadi standar untuk menguji endpoint API. Kalau kamu membangun API dengan FastAPI, kamu bisa memanfaatkan TestClient untuk mengirim request sungguhan tanpa perlu menjalankan server. Selengkapnya bisa kamu pelajari di tutorial cara membuat REST API dengan FastAPI. Polanya sama persis dengan yang sudah kita bahas: buat test, gunakan fixture untuk setup, lalu assert status code dan isi responsenya.
Kesimpulan
Pytest mengubah pengujian dari tugas yang membosankan menjadi kebiasaan yang menyenangkan. Dengan assert sederhana, fixtures, parametrize, dan mocking, kamu sudah punya fondasi yang kuat untuk menulis test otomatis di proyek Python apa pun. Mulailah dari test kecil di proyekmu hari ini, lalu rasakan sendiri betapa lebih percaya dirinya kamu saat mengubah kode.
Mulai dari yang sederhana: pilih satu fungsi kecil di proyekmu, tulis dua atau tiga test untuknya, lalu jalankan pytest. Kebiasaan kecil ini jauh lebih berharga daripada menunggu "proyek besar" untuk mulai testing.
Apa bedanya Pytest dengan unittest bawaan Python?
Pytest dan unittest sama-sama bisa menulis test, tetapi Pytest jauh lebih ringkas. Kamu cukup menulis fungsi biasa dengan assert, tanpa perlu membuat class turunan TestCase. Pytest juga punya fixtures dan parametrize yang lebih ekspresif, plus ekosistem plugin yang sangat kaya.
Apakah saya harus menulis test untuk setiap baris kode?
Tidak perlu. Fokuslah pada bagian yang paling penting: logika bisnis, fungsi yang sering berubah, dan titik integrasi antar modul. Cakupan kode (code coverage) 100% bukan tujuan utama — yang lebih penting adalah test yang berarti dan bisa diandalkan.