Penanganan error adalah fitur, bukan tambahan: bagi() yang menerima pembagi nol tanpa protes adalah bug yang menunggu waktu meledak di produksi. Karena itu jalur error layak diuji setelang jalur normal — dan caranya berbeda, karena nilai balik yang diperiksa bukan hasil hitungan melainkan exception yang dibangkitkan. Pada Chapter ini kita menguji exception dengan pytest.raises, memeriksa exception kustom beserta isinya, memastikan kode justru tidak error, lalu mencatat anti-pola yang membuat test error menjadi sia-sia.
Menguji Kode yang Seharusnya Gagal #
Sebagai konteks, inilah jalur error pada kalkulator.py yang sudah kita kenal dari awal Section:
class PembagianNolError(Exception):
def __init__(self, pembagi):
super().__init__(f"pembagi {pembagi} tidak boleh nol")
self.pembagi = pembagi
def bagi(a, b):
if b == 0:
raise PembagianNolError(b)
return a / bPerilaku kerjanya bisa dibuktikan langsung:
from kalkulator import bagi, PembagianNolError
try:
hasil = bagi(10, 0)
except PembagianNolError as e:
print("pesan:", e)
print("pembagi:", e.pembagi)Output yang Dihasilkan:
pesan: pembagi 0 tidak boleh nol
pembagi: 0Dalam test pytest, pembandingnya adalah pytest.raises sebagai context manager, dengan argumen match berisi pola regex:
import pytest
from kalkulator import bagi, PembagianNolError
def test_bagi_dengan_nol_menolak():
with pytest.raises(PembagianNolError, match=r"tidak boleh nol"):
bagi(10, 0)
def test_pesan_error_menyebut_pembagi():
with pytest.raises(PembagianNolError, match=r"pembagi 0"):
bagi(10, 0)
def test_bagi_dengan_string_menolak():
with pytest.raises(TypeError, match=r"unsupported operand"):
bagi(10, "nol")Tiga aturan kerja pytest.raises:
- Exception harus muncul di dalam blok
with— bilabagi(10, 0)selesai tanpa error, test justru gagal dengan pesanDID NOT RAISE. - Tipe harus cocok persis atau mewarisi tipe yang dinyatakan —
PembagianNolErrorcocok karena dituliskan eksplisit;Exceptiongenerik juga cocok, dan inilah sumber anti-pola yang kita bahas di bawah. matchdicocokkan denganre.searchterhadap pesan error — penulisanr"..."disarankan agar backslash regex tidak ditafsir ulang oleh string biasa.
Memeriksa Isi Exception Kustom #
match cukup untuk memeriksa pesan. Exception kustom sering membawa data tambahan sebagai atribut — rancangan mengikuti Chapter Membuat Custom Exceptions. Objek exception yang tertangkap bisa diakses lewat klausa as:
def test_error_menyimpan_pembagi():
with pytest.raises(PembagianNolError) as info:
bagi(7, 0)
assert info.value.pembagi == 0Setelah blok with selesai, info.value berisi instance exception yang tertangkap, lengkap dengan atribut pembagi yang diisi constructor. Test semacam ini memastikan kontrak data error tetap stabil bagi kode pemanggil.
Memastikan Tidak Ada Exception #
Kebalikannya juga penting: memastikan jalur normal tidak error. Tidak perlu alat khusus — cukup panggil kodenya:
def test_bagi_normal_tidak_membangkitkan_error():
hasil = bagi(10, 4)
assert hasil == 2.5Bila bagi() tanpa sengaja membangkitkan exception pada masukan normal, pytest menghentikan test dan menampilkan traceback exception itu — test otomatis gagal. Ketiadaan pytest.raises adalah pernyataan “kode ini harus selesai mulus”.
Output seluruh test di atas (run python3 -m pytest -q):
..... [100%]
5 passed in 0.01sAnti-Pola pada Test Exception #
Dua kebiasaan berikut membuat test error kehilangan daya verifikasinya:
import pytest
from kalkulator import bagi
# ANTI-POLA 1: tipe terlalu longgar
def test_bagi_dengan_nol_terlalu_longgar():
with pytest.raises(Exception):
bagi(10, 0)
# ANTI-POLA 2: try/except lalu diam
def test_bagi_dengan_nol_tanpa_verifikasi():
try:
bagi(10, 0)
except Exception:
pass- Tipe
Exceptiongenerik lulus untuk semua kegagalan — termasukTypeErroryang terjadi karena test salah menulis argumen. Test tidak lagi memastikan error yang benar; selalu nyatakan tipe paling spesifik (PembagianNolError), dan tambahkanmatchbila pesan berkontribusi pada kontrak. try/exceptlalupassbahkan tidak memverifikasi apa pun: bilabagi()berhenti membangkitkan error, test tetap lulus karena jalur normal juga tidak menyentuhexcept. Verifikasi keberadaan exception adalah tugaspytest.raises, bukantry/except.
Test kini menjaga kedua jalur perilaku — hasil normal dan penolakan error. Pertanyaan berikutnya mengukur: berapa banyak kode kita yang benar-benar tersentuh test? Jawabannya di Chapter Code Coverage.