Kodokon kodokon.com

अपने API का टेस्ट करना: node:test, fetch और एक टेस्ट डेटाबेस

अपने रूट का आद्योपांत (end to end) टेस्ट node:test और fetch से एक अस्थायी पोर्ट पर करें, जहाँ हर टेस्ट को अपना खुद का इन-मेमोरी SQLite डेटाबेस मिलता है।

9 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

Node 20 के साथ वह सब कुछ आता है जिसकी आपको ज़रूरत है: रनर के रूप में node:test, अभिकथनों के लिए node:assert/strict, HTTP क्लाइंट के रूप में ग्लोबल fetch। शून्य टेस्ट निर्भरताएँ। और यहीं पहले पाठ का आर्किटेक्चर रंग लाता है: createApp({ db }) एक इंजेक्ट किया गया डेटाबेस स्वीकार करता है, इसलिए हर टेस्ट कुछ ही मिलीसेकंड में एक पूर्ण, पृथक API माउंट कर सकता है।

BASH
node --test
node --test --watch
रनर परियोजना की *.test.js फ़ाइलों को खुद ही खोज लेता है।

रूट का आद्योपांत टेस्ट करने के लिए, Express को मॉक करने की कोई ज़रूरत नहीं: पोर्ट 0 पर एक असली सर्वर माउंट करें - सिस्टम एक खाली पोर्ट सौंप देता है - फिर उसे fetch से हिट करें। आप ठीक वही चीज़ आज़माते हैं जो एक असली क्लाइंट देखेगा: रूटिंग, मिडलवेयर, सीरियलाइज़ेशन, स्टेटस कोड। यह supertest की भावना है, बिना निर्भरता के।

JAVASCRIPT
import { once } from 'node:events';

export async function startTestServer(app) {
  const server = app.listen(0);
  await once(server, 'listening');
  const { port } = server.address();
  const url = `http://127.0.0.1:${port}`;
  const close = () =>
    new Promise((resolve) => {
      server.close(resolve);
    });
  return { url, close };
}
helpers.js: एक OS-द्वारा-सौंपे गए पोर्ट पर एक असली सर्वर।

टेस्ट डेटाबेस: मेमोरी में SQLite (:memory:)। हर टेस्ट createDb के ज़रिए अपना खुद का ताज़ा, खाली डेटाबेस बनाता है, जो स्कीमा लागू करता है, फिर बाहर निकलते समय उसे फेंक देता है - टेस्टों के बीच कोई डेटा साझा नहीं, कोई सफ़ाई स्क्रिप्ट नहीं। t.after इस बात की गारंटी देता है कि अभिकथन विफल होने पर भी सर्वर बंद हो जाए।

JAVASCRIPT
import test from 'node:test';
import assert from 'node:assert/strict';
import { createDb } from '../src/db/connection.js';
import { createApp } from '../src/app.js';
import { startTestServer } from './helpers.js';

test('POST /api/users creates a user', async (t) => {
  const db = createDb(':memory:');
  const app = createApp({ db });
  const server = await startTestServer(app);
  t.after(() => server.close());

  const res = await fetch(`${server.url}/api/users`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({
      email: 'ada@example.com',
      name: 'Ada',
    }),
  });

  assert.equal(res.status, 201);
  const body = await res.json();
  assert.equal(body.email, 'ada@example.com');
});
एक आद्योपांत टेस्ट: असली HTTP स्टैक, इस्तेमाल-के-बाद-फेंक डेटाबेस।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. टेस्ट सर्वर को app.listen(0) के साथ क्यों शुरू करें?
    • पोर्ट 0 नेटवर्क को बंद कर देता है और टेस्ट को तेज़ करता है
    • सिस्टम एक खाली पोर्ट सौंपता है: समानांतर टेस्ट कभी एक ही पोर्ट पर नहीं झगड़ते
    • प्रोडक्शन के बाहर यही एकमात्र अनुमत पोर्ट है
    • पोर्ट 0 Express को टेस्ट मोड में बाध्य कर देता है
  2. हर टेस्ट में बनाए गए :memory: डेटाबेस का निर्णायक लाभ क्या है?
    • यह तुलना के लिए रन के बीच डेटा को बनाए रखता है
    • हर टेस्ट एक खाली, पृथक स्थिति से शुरू होता है, बिना किसी सफ़ाई स्क्रिप्ट और टेस्टों के बीच बिना किसी हस्तक्षेप के
    • यह इंसर्शन को सरल बनाने के लिए SQL बाधाओं को बंद कर देता है
  3. सर्वर को टेस्ट बॉडी के अंत के बजाय t.after में क्यों बंद करें?
    • t.after अभिकथन विफल होने पर भी चलता है, जिससे ऐसे अनाथ सर्वर टलते हैं जो प्रोसेस को बाहर निकलने से रोकते हैं
    • t.after टेस्ट से पहले चलता है, जो सर्वर को तैयार करता है
    • यह विशुद्ध रूप से शैलीगत है, व्यवहार एक जैसा ही है