Assertion adalah kalimat keputusan dalam test: “saya menyatakan kondisi ini benar, dan hentikan test jika ternyata salah”. Pada Chapter Menulis Test dengan pytest kita sudah melihat assert polos menghasilkan laporan kegagalan yang rinci. Chapter ini membedah mekanisme di balik laporan tersebut (assert rewriting), melengkapi kotak peralatan dengan pytest.approx untuk angka pecahan dan pytest.raises untuk jalur error, lalu menutup dengan rancangan jumlah assertion per test beserta jebakan assert di dalam fungsi bantu.
Assert Rewriting di Balik Layar #
Ketika pytest mengimpor file test, pytest menulis ulang setiap pernyataan assert menjadi versi yang menyimpan nilai subekspresi — mekanisme ini bernama assert rewriting. Manfaatnya terlihat ketika test gagal. Perhatikan dua test sengaja salah berikut:
from kalkulator import bagi
def test_diskon_keranjang():
harga = 15000
jumlah = 2
total = harga * jumlah
assert total * 0.9 == 26000
def test_kondisi_gabungan():
stok = 3
ambang = 5
assert stok >= ambang and stok > 0Output yang Dihasilkan (dipangkas dari run python3 -m pytest -q):
> assert total * 0.9 == 26000
E assert (30000 * 0.9) == 26000
> assert stok >= ambang and stok > 0
E assert (3 >= 5)Baris berawalan E menampilkan lebih dari sekadar hasil True/False:
- Pada test pertama,
total * 0.9dicetak sebagai(30000 * 0.9)— pytest menyisipkan nilaitotalyang sudah dihitung, sehingga kita tahu total keranjang30000tanpa membuka debugger. - Pada test kedua, kondisi gabungan hanya menampilkan subekspresi yang gagal,
(3 >= 5); karena operatorandberhenti pada kegagalan pertama, subekspresistok > 0tidak pernah dievaluasi.
Angka Pecahan dengan pytest.approx #
Aritmetika pecahan biner tidak bisa menyimpan 0.1 secara persis, sehingga tambah(0.1, 0.2) == 0.3 bernilai False. Perbandingan langsung seperti ini akan membuat test gagal padahal kode sebenarnya benar. pytest menyediakan objek pytest.approx sebagai pembanding berbasis toleransi:
import pytest
from kalkulator import tambah, bagi
def test_bagi_hasil_pecahan_presisi():
assert bagi(1, 3) == pytest.approx(0.333, abs=0.001)
def test_bagi_sama_dengan_pecahan_murni():
assert bagi(1, 3) == pytest.approx(1 / 3)
def test_tambah_dengan_tolerance():
assert tambah(0.1, 0.2) == pytest.approx(0.3)Tiga bentuk pemakaian di atas:
abs=0.001mengatur toleransi absolut manual — selisih hingga0.001dianggap sama.- Tanpa argumen,
pytest.approxmemakai toleransi relatif otomatis, cocok membandingkan dua hasil hitung yang sama-sama berasal dari operasi pecahan. pytest.approx(0.3)menampung ketidakpresisian0.1 + 0.2yang sedikit menyimpang dari0.3.
Jalankan python3 -m pytest -q pada file tersebut (bersama test error di bawah), hasilnya:
....
4 passed in 0.01sMenyatakan Kegagalan dengan pytest.raises #
Assertion tidak selalu tentang nilai balik; kadang perilaku benar justru adalah menolak masukan. pytest.raises menjadi assertion untuk exception:
import pytest
from kalkulator import bagi, PembagianNolError
def test_bagi_dengan_nol_ditolak():
with pytest.raises(PembagianNolError, match="tidak boleh nol"):
bagi(10, 0)Blok with mengelilingi kode yang diharapkan gagal. Argumen match menerima pola regex yang dicocokkan ke pesan error — bila exception tidak muncul atau pesannya berbeda, test dinyatakan gagal. Pembahasan mendalam ada di Chapter Testing Exceptions.
Satu Test, Satu Assertion Logis #
Test ideal memverifikasi satu perilaku. Bukan berarti hanya satu baris assert — beberapa assert yang memeriksa aspek berbeda dari perilaku yang sama tetap sehat:
def test_total_keranjang():
keranjang.tambah_item("Kopi", 25000, 2)
assert keranjang.jumlah_item() == 2
assert keranjang.total() == 50000Menggabungkan banyak perilaku berbeda dalam satu test membuat laporan kegagalan kabur: test berhenti di assertion pertama yang salah, sementara perilaku setelahnya belum tentu benar. Bila kita perlu komentar “dan juga cek…” di tengah test, biasanya itu sinyal test harus dipecah.
Jebakan Assert di Fungsi Bantu #
Menaruh assert di dalam fungsi bantu menggeser lokasi pelaporan kegagalan. Perhatikan pola berikut:
from keranjang import KeranjangBelanja
def assert_total_valid(total, harapan):
assert total == harapan, "total keranjang tidak sesuai"
def test_total_keranjang():
keranjang = KeranjangBelanja()
keranjang.tambah_item("Kopi", 25000, 2)
assert_total_valid(keranjang.total(), 60000)Output yang Dihasilkan (dipangkas dari run python3 -m pytest -q):
def test_total_keranjang():
keranjang = KeranjangBelanja()
keranjang.tambah_item("Kopi", 25000, 2)
> assert_total_valid(keranjang.total(), 60000)
test_helper_salah.py:9:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
total = 50000, harapan = 60000
def assert_total_valid(total, harapan):
> assert total == harapan, "total keranjang tidak sesuai"
E AssertionError: total keranjang tidak sesuai
E assert 50000 == 60000
test_helper_salah.py:4: AssertionErrorLaporan kegagalan kini menunjuk baris 4 — baris di dalam fungsi bantu — bukan baris 9 tempat test memanggilnya. pytest tetap menampilkan kedua frame, tetapi membaca laporan butuh lompatan dua kali.
Dengan assertion yang tajam, kita bergerak ke persoalan berikutnya: menyiapkan data dan objek yang sama berulang kali di banyak test — pekerjaan fixtures pada Chapter Fixtures.