This episode teaches automated testing: unit testing with pytest, test fixtures, parametrization, and mocking, plus property-based testing with hypothesis. You'll also learn integration and end-to-end tests and test reliability strategies.

Code without tests is unproven code. Episode 18 equips you with pytest — the most popular testing framework in Python — and large-scale testing strategies: fixtures, parametrization, mocking, and property-based testing.
We'll also dissect integration and end-to-end tests, and how to keep tests reliable and fast. The goal is one thing: code changes don't break existing features.
pytest automatically finds files and functions starting with test_:
def hitung_diskon(harga, persen):
return harga * (1 - persen / 100)Save the function in kalkulasi.py, then create a test file:
from kalkulasi import hitung_diskon
def test_diskon_normal():
assert hitung_diskon(100000, 10) == 90000
def test_diskon_nol():
assert hitung_diskon(100000, 0) == 100000def test_diskon_normal(): defines a test with assertions. assert hitung_diskon(100000, 10) == 90000 checks the result. Run it with:
pip install pytest
pytest -vpytest -v runs all tests and shows detailed results. Green output means everything passed. pytest gives clear reports on failed assertions, including actual and expected values.
Fixtures prepare data and context for tests:
import pytest
@pytest.fixture
def pengguna_contoh():
return {"nama": "Arman", "usia": 30}
def test_profil(pengguna_contoh):
assert pengguna_contoh["nama"] == "Arman"
assert pengguna_contoh["usia"] == 30@pytest.fixture declares a fixture that can be injected into tests. The test function test_profil(pengguna_contoh) receives the fixture as a parameter. Fixtures are recreated per test, giving isolation between tests.
Parametrization runs one test with many inputs:
import pytest
from kalkulasi import hitung_diskon
@pytest.mark.parametrize(
"harga,persen,harapan",
[
(100000, 10, 90000),
(50000, 0, 50000),
(200000, 25, 150000),
],
)
def test_diskon(harga, persen, harapan):
assert hitung_diskon(harga, persen) == harapan@pytest.mark.parametrize("harga,persen,harapan", [...]) runs the test for every parameter combination. If one case fails, the others still run and the report shows which parameters failed. This replaces many similar test functions.
Mocks replace external dependencies — APIs, databases — with fake objects:
def panggil_api():
import urllib.request
return urllib.request.urlopen("https://httpbin.org/json")
def ambil_nama():
data = panggil_api()
return data.status
def test_ambil_nama(monkeypatch):
class Palsu:
status = 200
monkeypatch.setattr("urllib.request.urlopen", lambda url: Palsu())
assert ambil_nama() == 200monkeypatch.setattr("urllib.request.urlopen", lambda url: Palsu()) replaces the network function with a fake version. The test runs fast, without internet, and deterministically. monkeypatch undoes the change after the test finishes — isolation is preserved.
Hypothesis generates many inputs to find bugs:
pip install hypothesisfrom hypothesis import given
from hypothesis import strategies as st
def urutkan_numeric(angka):
return sorted(angka)
@given(st.lists(st.integers()))
def test_urut_selalu_sorted(angka):
hasil = urutkan_numeric(angka)
assert hasil == sorted(hasil)@given(st.lists(st.integers())) runs the test with hundreds of random integer lists. The property being tested: the result is always sorted. Hypothesis looks for inputs that break the property — far beyond manually chosen cases.
Arrange tests in a pyramid for speed and coverage:
E2E (sedikit)
Integration (sedang)
Unit test (banyak dan cepat)Unit tests are the most numerous because they're fast and cheap. Integration tests are fewer because they use real components. End-to-end tests are the fewest because they're slow and expensive. This balance keeps the test suite fast and meaningful.
Key takeaways:
In the next episode, episode 19, we'll cover packaging, distribution, and PyPI — the pyproject.toml standard, build backends like setuptools, Poetry, and Flit, building wheels, publishing with twine, plus versioning strategies and managing releases. Your library is ready to be shared with the world!