Ketika tim mulai menggunakan lingkungan pratinjau secara serius, masalah yang sama hampir selalu muncul: email dari satu branch tercampur dengan email dari branch lain. Pada FastAPI, hal ini sering terjadi ketika layanan email berada di luar request utama dan tidak ada yang meneruskan konteks yang cukup. Hasilnya biasanya tidak merusak produksi, tetapi merusak kepercayaan pada pengujian. Dalam proyek terbaru saya, saya melihat hal yang sangat mirip. Setiap pull request menjalankan API sendiri, menjalankan tes, dan memicu notifikasi pendaftaran, undangan, dan reset kata sandi. Semuanya tampak stabil, tetapi satu dari beberapa kali eksekusi gagal karena alasan yang sangat konyol: tes membaca email yang valid, tetapi email tersebut milik branch lain. Jika Anda menggunakan alamat email sekali pakai, pemisahan ini sangat penting. Hal ini juga membantu ketika anggota tim menggunakan kotak masuk sementara untuk mereproduksi bug tertentu dan tidak ingin mengejar pesan yang tercampur. Bahkan untuk kasus yang lebih spontan, seperti pengujian cepat dengan tempail mail, isolasi menghemat waktu dan mencegah kesimpulan yang aneh. Mengapa setiap branch membutuhkan kotak masuk sendiri? Ada tiga penyebab yang saya lihat berulang-ulang: worker email tidak menerima run_id atau nama branch, tes hanya mencari "email terakhir" dan mengasumsikan email tersebut milik mereka, dan pembersihan kotak masuk terjadi terlambat atau tidak pernah terjadi. Hal ini membuat suite pengujian menjadi rapuh. Jika Anda pernah membaca tentang pengujian email handoff, polanya mirip: sinyal yang berguna muncul ketika Anda menghubungkan pesan dengan event yang benar, bukan ketika Anda mencari email yang rapi di kotak masuk bersama. Aturan saya sederhana: satu branch, satu identitas yang dapat diamati. Tidak perlu membangun platform yang besar. Dengan memastikan setiap eksekusi memiliki run_id, tag lingkungan, dan kotak masuk yang terkait, Anda dapat mengurangi banyak gangguan. Pola sederhana: run_id + kotak masuk per lingkungan. Implementasi minimalnya terlihat seperti ini: Buat run_id di awal pipeline. Propagasi nilai tersebut ke backend, worker, dan test runner. Bangun penerima atau alias dengan pengenal yang sama. Filter pesan berdasarkan run_id sebelum memvalidasi subjek, isi, atau tautan. Jika tim Anda bekerja dengan kontrak event, Anda harus mempertimbangkan email sebagai output yang dapat diverifikasi dari API. Pendekatan ini mirip dengan kontrak email yang dapat diuji: pertama, Anda mengidentifikasi event yang benar, kemudian Anda memeriksa kontennya. Detail yang terkadang diabaikan: run_id juga harus ada di log dan metrik. Ketika seorang QA mengatakan "email tidak terkirim", Anda ingin dapat mencari pengenal tersebut di antrian, aplikasi, dan penyedia tanpa menebak-nebak. Contoh kecil dengan FastAPI Saya biasanya meneruskan konteks email sebagai bagian eksplisit dari perintah bisnis, bukan sebagai variabel global. Hal ini membuat pengujian lebih mudah dan kurang rentan terhadap drift. from fastapi import FastAPI from pydantic import BaseModel
app = FastAPI()
class SignupPayload(BaseModel): email: str run_id: str branch: str
async def send_signup_email(email: str, run_id: str, branch: str) -> None: inbox_tag = f"{branch}-{run_id}" subject = f"Konfirmasi akun Anda [{inbox_tag}]" # Di sini Anda akan memanggil penyedia sebenarnya atau adapter yang dapat diuji. print({"to": email, "subject": subject, "run_id": run_id})
@app.post("/signup") async def signup(payload: SignupPayload): await send_signup_email( email=payload.email, run_id=payload.run_id, branch=payload.branch, ) return {"ok": True}
Yang penting bukan print-nya, tentu saja. Yang penting adalah bahwa konteks tidak hilang. Kemudian, dalam pengujian Anda, Anda mencari pesan berdasarkan run_id dan mengonfirmasi bahwa subjek dan tautannya sesuai dengan eksekusi tersebut. Ini tampak jelas, tetapi banyak aplikasi masih menyembunyikan data ini di middleware atau variabel proses yang tidak divalidasi dengan baik. Apa yang harus diperiksa di CI sebelum disetujui Ketika pola ini sudah ada, daftar periksa saya biasanya singkat: Setiap job membuat run_id yang unik. Backend menyimpan atau meneruskannya tanpa mengubahnya. Worker menyertakan run_id dalam log dan metadata pesan. Tes memfilter berdasarkan run_id sebelum membaca konten. Pembersihan kotak masuk sementara dijalankan di akhir, bahkan jika job gagal. Pembersihan lebih penting daripada yang terlihat. Menurut laporan kualitas perangkat lunak CISQ, biaya perangkat lunak yang buruk di AS mencapai 2,41 triliun dolar pada tahun 2022. Tidak semuanya berasal dari email, tentu saja, tetapi hal ini mengingatkan kita bahwa cacat kecil dan berulang akhirnya menjadi mahal. Anda juga harus memeriksa waktu. Jika penyedia Anda memberikan pesan dengan latensi yang bervariasi, jangan mengubah penundaan normal menjadi false negative. Polling singkat dengan timeout yang wajar biasanya lebih baik daripada tidur selama 30 detik "hanya untuk berjaga-jaga". Trik ini berhasil... sampai tidak berhasil lagi. Pertanyaan umum Apakah saya selalu membutuhkan kotak masuk yang berbeda untuk setiap branch? Tidak harus kotak masuk fisik yang berbeda. Kadang-kadang cukup dengan alias, label, atau filter berdasarkan metadata. Yang Anda butuhkan adalah pemisahan yang dapat diamati dan konsisten. Apakah hal ini tidak terlalu rumit untuk API kecil? Sedikit, tetapi lebih baik daripada mengalami flakes selama berminggu-minggu. Selain itu, setelah Anda memodelkan konteks email, bagian backend lainnya menjadi jauh lebih jelas. Apakah sebaiknya saya memasukkan nama branch dalam subjek? Untuk lingkungan non-produksi, ya. Hal ini sangat membantu untuk debugging manual. Pastikan hal ini tidak menjadi ketergantungan utama dari tes; filter yang serius harus tetap menggunakan run_id. Jika pengujian email FastAPI Anda saat ini bergantung pada "mencari pesan terakhir", saya akan mulai dari sini. Ini bukan arsitektur yang eksotis atau sempurna, tetapi ini adalah peningkatan nyata yang biasanya terasa pada hari yang sama.