Skip to main content
Skill web app · Pelajaran 8 dari 11 · 14 menit

Tes Situs Next.js dengan node:test dan Build Gate

Tes situs Next.js tanpa Jest: contract test node:test, prebuild gate seperti assertBlogParity, dan cara memutuskan apa yang dijaga tes dan gate.

Oleh Matthews WongDitinjau pada

Poin utama

Contract test memeriksa bahwa data atau fungsi murni Anda masih mematuhi sebuah aturan, dan test runner bawaan node:test sudah cukup untuk menulisnya. Build gate adalah skrip yang menggagalkan build ketika sebuah invarian rusak. Pakai tes untuk perilaku dan kontrak konten, dan gate untuk invarian murah yang tidak boleh sampai ke produksi.

Setelah pelajaran ini, Anda bisa

  • Menulis contract test berbasis data dengan node:test dan menjalankannya dengan node --test.
  • Menulis prebuild gate yang gagal dengan pesan yang menyebut cara memperbaikinya.
  • Memutuskan apakah sebuah aturan masuk ke tes, gate, atau pengecekan live.
Lihat cara kerjanya

Ikuti diagramnya langkah demi langkah

Tekan Putar, atau melangkah dengan panah. Setiap langkah juga ditulis di bawah diagram.

Satu perubahan, tiga jaring pengaman

Perubahan data bertemu npm test, lalu prebuild gate, lalu build dengan assertBlogParity, dan terakhir agents:check di situs live, masing-masing gagal di tempat dan dengan pesan yang berbeda.

Langkah 1 dari 6. Empat pos pemeriksaan berdiri di antara sebuah edit dan pengunjung. Masing-masing murah, cepat, dan mengawasi hal yang berbeda.

Tombol: ← → langkah · Home / End · K putar atau jeda

  1. Empat pos pemeriksaan berdiri di antara sebuah edit dan pengunjung. Masing-masing murah, cepat, dan mengawasi hal yang berbeda.

  2. Anda mengedit Dockerfile yang dikutip sebuah pelajaran. npm test mengimpor setiap pelajaran dan menjalankan satu pengecekan per pelajaran terhadap datanya.

    Tebak langkah berikutnya Pelajaran masih mengutip baris Dockerfile yang lama. Apa yang menangkapnya lebih dulu? (Tes kutipan di npm test · Build produksi · Tidak ada, langsung rilis)
  3. Kutipan tidak lagi cocok dengan file, jadi sebuah assertion melempar error, tes gagal, dan proses keluar dengan kode 1. Pesannya menyuruh memperbarui pelajaran.

    node --test tests/learn-content.test.mjs
  4. Setelah diperbaiki, prebuild menjalankan skrip Node biasa sebelum build dimulai. Salah satunya membandingkan file hasil generate dengan yang akan dihasilkan sumber saat ini.

    node scripts/generate-blog-sources.mjs --checkTebak langkah berikutnya File sitasi hasil generate sudah usang. Apa yang dilakukan gate? (Build lanjut dengan sitasi lama · Keluar dengan kode 1 dan mencetak perintah yang harus dijalankan)
  5. Gate mencetak bahwa file usang beserta perintah untuk meregenerasinya, lalu keluar dengan kode 1. Build tidak pernah dimulai, jadi tidak ada yang usang yang bisa dirilis.

    process.exit(1);
  6. Setelah diregenerasi, gate lolos. Build menegaskan paritas post saat merender sitemap, dan agents:check kemudian menguji situs yang berjalan lewat HTTP.

    assertBlogParity();

Bisakah node:test menggantikan Jest untuk situs Next.js?

Untuk banyak situs, bisa. Node.js membawa test runner sendiri: Anda mengimpor test, atau describe dan it, dari node:test, dan sebuah tes lulus kecuali ia melempar error, mengembalikan promise yang ditolak, atau melaporkan error. Dokumentasi menandai runner ini stabil sejak Node 20. Anda menjalankannya dengan node --test, yang secara default mengambil file bernama seperti *.test.mjs, dan jika ada tes yang gagal, exit code proses adalah 1.

Baca selengkapnya (1)

Repo ini tidak punya dependensi framework tes. Tesnya ada di tests/ sebagai file .test.mjs, assertion berasal dari modul assert milik Node, dan satu npm script menjalankannya. Karena modul data TypeScript bukan JavaScript biasa, setiap file tes lebih dulu memuat resolver kecil, sehingga bisa mengimpor file lib yang asli, bukan salinan. Header tes mencatat bahwa ini membutuhkan Node 22.18 atau lebih baru.

Apa yang sebaiknya dijaga oleh sebuah tes?

Tes cocok untuk perilaku yang bisa digambarkan sebagai input dan output. Fungsi header Accept adalah contohnya: tabel string header dan jawaban yang diharapkan, satu tes dibuat per baris. Header browser, bintang-slash-bintang polos, text/markdown dengan q=0, dan header kosong adalah kasus yang sulit direproduksi manual tetapi nyaris gratis disimpan dalam tabel.

Baca selengkapnya (2)

Tes juga cocok untuk kontrak konten. Kursus /learn adalah data, jadi satu file tes melakukan loop ke setiap file pelajaran dan membuat pengecekan untuk masing-masing: slug sama dengan nama file, judul muat dalam batas SERP, kuis punya tiga pertanyaan, dan setiap kutipan kode harus masih muncul persis sama di file aslinya. Ubah Dockerfile dan pelajaran yang mengutipnya gagal sampai seseorang memperbaruinya. Itu deteksi drift yang tidak akan tertangkap mata reviewer.

Next.js juga menyediakan helper sendiri. Dokumentasi menjelaskan fungsi eksperimental unstable_doesProxyMatch di next/experimental/testing/server yang menegaskan apakah middleware akan berjalan untuk sebuah URL, sehingga perubahan matcher bisa dikunci oleh tes. Fungsinya bertanda eksperimental, jadi anggap opsional.

Lalu apa yang sebaiknya dijaga oleh build gate?

Gate menjaga invarian yang harus berlaku agar build layak dirilis, dan harus murah serta ringan dependensi. Skrip prebuild repo ini merangkai skrip Node kecil: satu memeriksa file sitasi hasil generate masih terbaru, satu memastikan setiap proyek punya penempatan di game, satu memastikan mesin 3D hanya dijangkau lewat batas lazy, dan satu memastikan pohon key terjemahan cocok. Masing-masing keluar dengan kode bukan nol dan pesan, yang menghentikan build.

Baca selengkapnya (2)

Gate juga bisa hidup di kode aplikasi. assertBlogParity melempar error jika registri post dan tabel metadatanya mencakup slug yang berbeda, dan juga jika tanggal yang ditampilkan kartu bertentangan dengan tanggal di structured data. Ia berjalan saat sitemap, feed, dan llms.txt dibangun, sehingga registri yang rusak menggagalkan build alih-alih merilis sitemap yang salah. Pengecekan tanggal itu ada karena membandingkan himpunan slug saja pernah membiarkan 168 post menyimpang.

Kenapa skrip biasa, bukan tes? Gate placement menyebutnya sendiri: image build memakai Node 20 yang tidak bisa mengimpor modul data TypeScript, jadi ia membaca slug dengan regex. Itu membuatnya bebas dependensi dan bisa berjalan di mana pun build berjalan. Tes yang membutuhkan Node lebih baru dan resolver tidak akan berjalan di sana.

Bagaimana memutuskan antara tes, gate, dan pengecekan live?

Ajukan tiga pertanyaan. Apakah rusaknya hal ini akan diam-diam merilis situs yang buruk? Jadikan gate, karena build yang gagal tidak bisa dirilis. Apakah ia butuh impor yang kaya, banyak kasus, atau penjelasan maksud? Tulis tes, tempat tabel dan loop terasa alami. Apakah ia bergantung pada perilaku HTTP yang nyata, seperti header yang benar-benar dikirim server terdeploy? Itu pengecekan live: tes unit sendiri menyatakannya, dan skrip terpisah, agents:check, menguji situs yang berjalan.

Baca selengkapnya (1)

Apa pun pilihan Anda, buat kegagalannya mengajarkan perbaikannya. Gate file usang mencetak nama file dan perintah persis untuk meregenerasinya. Pengecekan paritas mencetak selisih slug. Kegagalan yang menyebut apa yang harus dijalankan berikutnya mengubah build yang terblokir menjadi pekerjaan tiga puluh detik.

Cara situs ini melakukannya

Konfigurasi nyata di balik pelajaran ini

Kutipan dari repositori yang membangun dan men-deploy situs ini, apa adanya.

package.json:10-13
    "test": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --test tests/",
    "agents:check": "node scripts/check-agent-readiness.mjs",
    "prebuild": "node scripts/generate-blog-sources.mjs --check && node scripts/game/check-placements.mjs && node scripts/game/check-imports.mjs && node scripts/check-message-keys.mjs",
    "sources:build": "node scripts/generate-blog-sources.mjs",

Tiga jaring berbeda dalam empat baris. test menjalankan contract test dengan node --test, prebuild merangkai gate dengan && sehingga kegagalan pertama menghentikan semuanya, dan agents:check adalah pengujian sesuai permintaan untuk situs yang berjalan. Baris sources:build adalah perbaikan yang ditunjuk gate file usang.

scripts/generate-blog-sources.mjs:105-111
if (CHECK_ONLY) {
  if (next !== previous) {
    console.error(
      `FAIL ${path.relative(ROOT, OUT)} is stale — run: node scripts/generate-blog-sources.mjs`,
    );
    process.exit(1);
  }

Gate kesegaran dalam enam baris. Ia meregenerasi konten di memori, membandingkannya dengan file yang di-commit, dan jika berbeda mencetak file serta perintah perbaikannya sebelum keluar dengan kode 1. Skrip yang sama menulis file itu saat dijalankan tanpa flag.

lib/blog-meta.ts
/**
 * Build-time guard: BLOG_POSTS_STATIC (which posts exist) and BLOG_META
 * (their SEO metadata) must cover exactly the same slugs. Throws with the
 * diff so a divergent build fails instead of shipping a broken sitemap.
 */
export function assertBlogParity(): void {

Gate di dalam kode aplikasi. Ia dipanggil di tempat sitemap, feed, dan llms.txt dihasilkan, sehingga build tidak bisa selesai dengan dua registri yang saling berbeda. Error-nya mencantumkan selisih, bukan sekadar jumlah.

tests/agent-readiness.test.mjs:52-56
  for (const [accept, expected] of cases) {
    it(`${JSON.stringify(accept)} -> ${expected ? "markdown" : "html"}`, () => {
      assert.equal(prefersMarkdown(accept), expected);
    });
  }

Tes berbasis data: loop mengubah setiap baris tabel cases menjadi tes bernama sendiri. Menambah kasus header cukup satu baris data, dan kegagalan menyebut persis header yang rusak.

scripts/game/check-placements.mjs:10-12
 * The data modules are TypeScript and the build image runs Node 20, which
 * cannot import them; the slugs are read with a regex instead. That is
 * enough for a parity check and keeps this script dependency-free.

Kenapa gate berupa skrip biasa. Ia harus berjalan di tempat build berjalan, dan lingkungan itu tidak bisa memuat data TypeScript, jadi ia menukar sedikit ketepatan dengan nol dependensi.

Yang sering rusak

Kesalahan yang benar-benar membuang waktu

  • Gate yang lolos padahal datanya salah

    Membandingkan himpunan slug saja membiarkan 168 tanggal post menyimpang, karena setiap nilai valid sendiri-sendiri. Saat dua sumber harus sepakat, periksa nilai yang mereka bagi, bukan sekadar keduanya ada.

  • Tes yang butuh Node lebih baru dari build

    Contract test butuh Node 22.18 atau lebih baru, sedangkan image build memakai Node 20. Taruh apa pun yang harus ditegakkan build di skrip biasa yang bisa dijalankan Node 20, dan simpan tes berbasis resolver untuk npm test.

  • Pesan gagal yang tidak menyebut apa yang harus dilakukan

    Exit 1 tanpa penjelasan membuat orang mencari-cari. Sebutkan file, aturan, dan perintah yang memperbaikinya, seperti yang dilakukan gate file usang.

  • Memalsukan sesuatu yang bisa dijawab file asli

    Tes dengan salinan Dockerfile tidak bisa menyadari Dockerfile berubah. Baca file aslinya dan bandingkan, seperti tes kutipan.

Tiga jaring pengaman di repo ini, dan fungsinya masing-masing
Dijalankan olehnpm test, sesuai kebutuhanprebuild atau build itu sendiriagents:check, sesuai kebutuhan
MenjagaPerilaku, kontrak data, driftInvarian yang tidak boleh dirilis rusakJawaban server asli lewat HTTP
MembutuhkanResolver TypeScript, Node 22.18+Hanya Node, berjalan di buildSitus yang berjalan
Coba sendiri

Lakukan di komputer Anda sendiri

Tulis satu tes berbasis data dan satu gate untuk sesuatu di proyek Anda sendiri.

  1. Buat tests/slug.test.mjs. Taruh kasus di sebuah array dan biarkan loop membuat satu tes bernama per kasus.

    import assert from "node:assert/strict";
    import { describe, it } from "node:test";
    
    const cases = [["Hello World", "hello-world"], ["  a  b ", "a-b"]];
    
    describe("slugify", () => {
      for (const [input, expected] of cases) {
        it(`${JSON.stringify(input)} -> ${expected}`, () => {
          assert.equal(slugify(input), expected);
        });
      }
    });
  2. Jalankan dengan node --test, lalu rusak satu nilai yang diharapkan dan baca kegagalannya. Pastikan exit code-nya 1.

    node --test tests/
    echo $?
  3. Tulis sebuah gate: skrip yang membangun ulang file hasil generate di memori, membandingkannya dengan yang di-commit, dan keluar dengan kode 1 beserta perintah perbaikan saat berbeda.

  4. Rangkai gate itu di skrip prebuild agar berjalan sebelum setiap build, lalu ubah sumbernya tanpa regenerasi dan lihat build berhenti.

    "prebuild": "node scripts/check-generated.mjs"
Kuis singkat

Tiga pertanyaan sebelum lanjut

Pilih jawaban untuk melihat kenapa benar atau salah. Tidak ada yang disimpan atau dinilai, jadi tebak saja dengan bebas.

  1. 1.Sebuah aturan harus berlaku untuk setiap build, dan pengecekannya hanya bisa memakai Node 20 biasa. Di mana tempatnya?
  2. 2.Kenapa satu file tes melakukan loop ke setiap pelajaran, bukan menguji satu pelajaran secara manual?
  3. 3.Apa yang membuat gate file usang mudah dipatuhi saat gagal?
Pikirkan dulu

Jawab dalam hati, lalu cek

  1. 1.Kenapa assertBlogParity mendapat pengecekan tanggal selain membandingkan slug?Lihat jawabannya

    Jawaban:Mencocokkan himpunan slug tidak cukup: 168 post menampilkan tanggal yang bertentangan dengan tanggal di structured data, dan setiap nilai valid sendiri-sendiri. Gate sebaiknya membandingkan nilai bersama yang bisa berbeda.

  2. 2.Sebuah tes gagal ketika kutipan tidak lagi cocok dengan filenya. Apakah itu bug di tes?Lihat jawabannya

    Jawaban:Bukan, itulah tujuannya. Pelajaran mengutip file asli, jadi saat file berubah pelajaran itu usang, dan kegagalan memaksa seseorang memperbaruinya.

  3. 3.Dari tiga jaring itu, mana yang Anda pakai untuk header yang benar-benar dikirim server terdeploy?Lihat jawabannya

    Jawaban:Pengecekan live. Tes unit hanya menjalankan kode, jadi repo ini menyimpan skrip terpisah, agents:check, yang membuat request HTTP nyata ke situs yang berjalan.

Pelajari lebih dalam

Bahasan mendalam terkait di situs ini