JSON Toolkit

JSON Schema कॉन्ट्रैक्ट टेस्टिंग: वैलिडेशन नियमों को CI में वेल्ड करना

मुख्य बात: स्कीमा लिखते ही खत्म नहीं होता — वहीं से गिरावट शुरू होती है। एक टाइपो चुपचाप वैलिडेशन ढीला कर देता है, एक रिफैक्टरिंग सारे फ़िक्सचर तोड़ देती है। यह गाइड एक चलाने योग्य कॉन्ट्रैक्ट टेस्टिंग सिस्टम की कली खोलती है: पॉज़िटिव, नेगेटिव और स्नैपशॉट फ़िक्सचर, स्कीमा का अपना lint, पुनःउपयोग के साथ एक बार कंपाइल, और CI के चार गेट जो कॉन्ट्रैक्ट तोड़ने वाले बदलावों को लाल बिल्ड में बदल देते हैं।

मध्यम~8 मिनट पढ़ें2026-11-05 को प्रकाशित2026-11-05 को अपडेट किया गया
JSON Schema कॉन्ट्रैक्ट टेस्टिंग और CI वैलिडेशन गेट का आरेख

निष्कर्ष: स्कीमा कोड है, और कोड को टेस्ट चाहिए। पॉज़िटिव फ़िक्सचर साबित करते हैं कि वह जो गुज़रना चाहिए उसे गुज़रने देता है; नेगेटिव साबित करते हैं कि वह जो रोकना चाहिए उसे रोकता है; स्नैपशॉट अनजाने बदलावों पर नज़र रखते हैं। फिर ajv compile और --ci के साथ स्नैपशॉट आपकी पाइपलाइन में रात की पहरेदारी करते हैं।

जब वैलिडेशन नियम टूटते हैं तो कोई एक्सेप्शन नहीं फेंकता: स्वीकृति का फैलना किसी को सूझता नहीं, और अस्वीकृति का कसना पूरी टीम चुकाती है। स्कीमा कॉन्फ़िगरेशन फ़ाइल नहीं है — वह कोड है जिसे कोई टेस्ट नहीं करता।

1. स्कीमा टूटता है — सबसे पहले कौन जानता है?

उस जाल से शुरू करते हैं जिस पर हमने सच में पैर रखा। ड्राफ्ट 2020-12 के अनुसार स्कीमा घोषित करें और Ajv के डिफ़ॉल्ट पैकेज से कंपाइल करें — एक्सेप्शन तुरंत उछलता है:

Error: no schema with key or ref "https://json-schema.org/draft/2020-12/schema"

Ajv 8 का डिफ़ॉल्ट एंट्री पॉइंट सिर्फ draft-07 जानता है; 2020-12 के लिए ajv/dist/2020 से इम्पोर्ट करना पड़ता है। दूसरा जाल: strict मोड में, ajv-formats इंस्टॉल किए बिना format: "email" लिखना कंपाइलेशन गिरा देता है:

Error: unknown format "email" ignored in schema at path "#/properties/email"

ये एरर पहले कंपाइलेशन में लोकल देख लेना अच्छी बात है। समस्या यह है: बहुत से प्रोजेक्ट कंपाइलेशन को रिक्वेस्ट आने तक टाल देते हैं, और टूटा स्कीमा अपने पहले शिकार का इंतज़ार करता है। और भी चालाक है आधा-टूटा स्कीमा: required गलती से ऐरे की जगह स्ट्रिंग लिखा जाए तो कंपाइल पर schema is invalid: data/required must be array आता है — पर अगर वह स्टेप CI में कभी चला ही नहीं, तो वह प्रोडक्शन में पड़ा रहता है।

इसलिए पहला गेट डेटा वैलिडेट नहीं करता: वह स्कीमा को ही वैलिडेट करता है। एक कमांड:

node scripts/lint-schemas.mjs
// scripts/lint-schemas.mjs
import { readFileSync, readdirSync } from 'node:fs';
import Ajv2020 from 'ajv/dist/2020.js';
import addFormats from 'ajv-formats';

const ajv = new Ajv2020({ allErrors: true, strict: true });
addFormats(ajv);
for (const file of readdirSync('schemas')) {
  const schema = JSON.parse(readFileSync(`schemas/${file}`, 'utf8'));
  ajv.compile(schema);
  console.log(`ok ${file}`);
}

npx ajv compile क्यों नहीं: ajv v8 पैकेज में CLI नहीं है, इसलिए npx ajv could not determine executable to run से फेल होता है; CLI अलग पैकेज ajv-cli में है (हमारे माप में 5.0.0)। पर ajv-cli 5 सिर्फ draft-07 और draft-2019-09 सपोर्ट करता है — खंड 1 की 2020-12 स्कीमा पर चलाने से वही no schema with key or ref "https://json-schema.org/draft/2020-12/schema" मिलता है। इसलिए 2020-12 प्रोजेक्ट लेख वाले Ajv2020 से ही lint करता है; npx ajv-cli compile -s '**/*.schema.json' --strict केवल draft-07 प्रोजेक्ट के लिए है।

इसे लिंटर की तरह चलाएँ: टाइपो, पुराने कीवर्ड और अनरजिस्टर्ड फ़ॉर्मैट PR चरण में ही दिख जाते हैं। यही पहली समानता है स्कीमा टेस्टिंग और साधारण कोड टेस्टिंग के बीच: पहले पक्का करें कि वह कंपाइल होता है।

2. त्रयी: पॉज़िटिव, नेगेटिव, स्नैपशॉट

स्कीमा टेस्टिंग का ढाँचा तीन टुकड़ों का है।

पॉज़िटिव फ़िक्सचर: असली रिस्पॉन्स से उठाए गए सैंपल जो पास होने चाहिए। उन्हें स्टेजिंग से लें, याददाश्त से नहीं — हाथ से लिखे पॉज़िटिव सिर्फ happy path ढकते हैं और null, खाली स्ट्रिंग और सिर्फ-आवश्यक-फ़ील्ड वाले ऑब्जेक्ट की सीमा भूल जाते हैं।

नेगेटिव फ़िक्सचर: स्कीमा के हर बाध्यक्रम के लिए कम से कम एक उल्लंघनकारी इनपुट। minimum: 0 के लिए जोड़ा -1 (अस्वीकृत) और 0 (स्वीकृत); enum के लिए सूची से बाहर का मान; additionalProperties: false के लिए फ़ालतू फ़ील्ड। नेगेटिव पॉज़िटिव से भारी होते हैं: जो स्कीमा चुपचाप खराब डेटा गुज़रने दे, वह साइलेंट बग है जो डेटाबेस तक दूषित करता है।

नामकरण की एक परंपरा जो फ़ाइल के नाम को ही असर्शन बना देती है, ओपन सोर्स से उधार ली जा सकती है:

schemas/order.schema.json
fixtures/order/valid-minimal.json        # पास होना चाहिए
fixtures/order/valid-full.json           # पास होना चाहिए
fixtures/order/invalid-type.json         # अस्वीकृत होना चाहिए
fixtures/order/invalid-extra-field.json  # अस्वीकृत होना चाहिए

Node में बने node:test से काम चल जाता है:

import { test } from 'node:test';
import { readFileSync, readdirSync } from 'node:fs';
import assert from 'node:assert/strict';
import Ajv2020 from 'ajv/dist/2020.js';
import addFormats from 'ajv-formats';

const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);
const validate = ajv.compile(
  JSON.parse(readFileSync('schemas/order.schema.json', 'utf8'))
);

for (const f of readdirSync('fixtures/order')) {
  const data = JSON.parse(readFileSync(`fixtures/order/${f}`, 'utf8'));
  if (f.startsWith('valid-')) {
    test(`${f} पास होना चाहिए`, () => { assert.ok(validate(data), JSON.stringify(validate.errors)); });
  } else {
    test(`${f} अस्वीकृत होना चाहिए`, () => { assert.ok(!validate(data)); });
  }
}

स्नैपशॉट: validate.errors को फ़ाइल में सीरियलाइज़ करें और तुलना करें। कोई अनजाने में बाध्यक्रम बदल दे — स्नैपशॉट तुरंत लाल हो जाता है। Jest/Vitest में एक लाइन: expect(errors).toMatchSnapshot(); CI में हमेशा --ci दें: नया स्नैपशॉट तब फेलियर होता है और चुपचाप नहीं बनता, और ड्रिफ्ट को कोई छूट नहीं मिलती।

3. एक ही रन में सारे एरर जमा करें

नेगेटिव टेस्टिंग करते समय भूलने वाली एक सेटिंग: वैलिडेटर डिफ़ॉल्ट रूप से पहले एरर पर रुक जाता है। डेटा तीन जगह टूटा है, आप एक देखते हैं, ठीक करते हैं, दोबारा चलाते हैं — और यहीं से आगे। allErrors: true (Ajv) या iter_errors (Python) से सब एक साथ दिखता है:

from jsonschema import Draft202012Validator

validator = Draft202012Validator(schema)
errors = sorted(validator.iter_errors(bad), key=lambda e: list(e.absolute_path))
for e in errors:
    print(list(e.absolute_path), e.validator, e.message)

Python एक अंतर्ज्ञान-विरोधी तथ्य भी सँजोए रखता है: validate() जो एरर रिपोर्ट करता है वह "पहला" नहीं बल्कि best_match की पसंद है। हमारे माप में, तीन उल्लंघनों के साथ (id का टाइप गलत, role सूची से बाहर, फ़ालतू फ़ील्ड extra), सिर्फ additionalProperties रिपोर्ट हुआ — id का टाइप-एरर छिपा रहा। CI में हमेशा iter_errors इस्तेमाल करें ताकि पूरी तस्वीर मिले।

और उससे भी सरल भूलने वाला जाल: format डिफ़ॉल्ट रूप से असर्शन नहीं है। ड्राफ्ट 2020-12 फ़ॉर्मैट को एनोटेशन श्रेणी देता है: Python का jsonschema बिना format_checker पास किए उसे वैलिडेट नहीं करता — हमारे टेस्ट में "not-an-email" खुले गुज़र गया। Ajv ajv-formats इंस्टॉल और रजिस्टर करने की माँग करता है। अगर फ़ॉर्मैट मायने रखता है (ईमेल, तारीख, uuid), तो एक नेगेटिव फ़िक्सचर होना चाहिए जो साबित करे कि आपके वैलिडेटर का कॉन्फ़िगरेशन उसे सच में जाँचता है।

4. एक बार कंपाइल करें — कंपाइलर को डिस्पोजेबल न मानें

लोकल माप (Node v22.13.1, Ajv 8.17, वही तीन-स्तरीय नेस्टेड स्कीमा, वही उल्लंघनकारी डेटा):

दृष्टिकोण दस हज़ार वैलिडेशन का समय
हर बार new Ajv() फिर compile 42233 ms
मॉड्यूल-स्तरीय इंस्टेंस + compile एक बार 6.9 ms

6000 गुना। रिक्वेस्ट हैंडलर के भीतर वह वैलिडेटर जो हर कॉल पर नया Ajv बनाता है, सबसे आम परफ़ॉर्मेंस दुर्घटना है। फैली हुई गलत जवाबी भी नोट करें: "ajv.validate(schema, data) हर बार दोबारा कंपाइल करता है"। मापा गया: एक लाख कॉल में 8.2 ms लगे — एक बार कंपाइल हुई फ़ंक्शन के 11.2 ms के करीब, क्योंकि Ajv स्कीमा को आंतरिक कैश में रखता है। असली बीमारी कैश-मिस नहीं, बल्कि बिना पुनःउपयोग वाला इंस्टेंस है। Python में भी वैसा ही: पुनःउपयोग होने वाला Draft202012Validator हर बार validate() कॉल करने से लगभग 5000 गुना तेज़ है (2000 इटरेशन: 0.3 ms बनाम 1566 ms), क्योंकि उत्तरार्द्ध हर कॉल पर स्कीमा को ही जाँचता है।

5. गेट को CI में वेल्ड करें: चार दरवाज़े

सस्ते से महँगे क्रम में: पहला गिरे तो बाकी चलाने की ज़रूरत नहीं:

गेट कमांड रूप क्या पकड़ता है लागत
स्कीमा lint node scripts/lint-schemas.mjs सिंटैक्स एरर, पुराने कीवर्ड मिलीसेकंड
फ़िक्सचर वैलिडेशन खंड 2 का टेस्ट सूट फ़िक्सचर/स्कीमा असंगति, गलत चिह्नित बाध्यक्रम मिलीसेकंड
तोड़ने वाले बदलावों का diff json-schema-diff-validator बनाम main required हटा, enum सिकुड़ा, pattern कठोर हुआ सेकंड
असली रिस्पॉन्स वैलिडेशन staging पर .toMatchSchema() असर्शन के साथ स्मोक टेस्ट असली एंडपॉइंट का ड्रिफ्ट सेकंड

GitHub Actions का न्यूनतम कॉन्फ़िगरेशन:

- run: npm ci
- run: npm run lint:schemas        # Ajv2020 lint
- run: npm run test:schema         # फ़िक्सचर त्रयी
- run: npx jest --ci --testPathPattern=schema   # स्नैपशॉट ड्रिफ्ट का पहरेदार

तोड़ने वाले बदलावों का गेट अलग से ज़ोर देने लायक है: json-schema-diff-validator जैसे टूल "required हटाया गया" और "enum के मान गायब" को रिव्यू के क्षण ही लाल कर देते हैं — ठीक वे दो बदलाव जिन्हें साधारण API-diff "तोड़ता नहीं" आँकने लगता है। रिस्पॉन्स से फ़ील्ड हटाना सहिष्णु उपभोक्ता के लिए बेनिंग और विस्तृत switch वाले उपभोक्ता के लिए घातक है।

6. बूलियन नहीं, एरर ऑब्जेक्ट पर असर्शन करें

नेगेटिव टेस्ट जो सिर्फ validate(data) === false देखे, वह कम पड़ता है। उसी स्कीमा के लिए पक्का करें कि वह सही कारण से अस्वीकार करता है: कोई अन्य बाध्यक्रम संयोग से पास हो सकता है। Ajv के एरर ऑब्जेक्ट की एक प्रसिद्ध असममिता है: जब required फेल होता है, instancePath पैरेंट ऑब्जेक्ट की ओर इशारा करता है, और गायब फ़ील्ड का नाम params.missingProperty में छिपता है। id के अनुपस्थित होने पर मापा गया:

{ "instancePath": "", "schemaPath": "#/required",
  "keyword": "required", "params": { "missingProperty": "id" } }

असर्शन लिखा जाता है "keyword=required और missingProperty=id वाला एरर मौजूद है", धुंधला errors.length > 0 नहीं। एरर फ़ील्ड की पूरी सिमेंटिक्स का विघटन JSON Schema के अपठनीय एरर: Ajv में एरर की जगह और मैसेज का प्रबंधन में है।

7. आम जाल — त्वरित तालिका

जाल परिणाम इलाज
सिर्फ happy path के 200 टेस्ट करना एरर रिस्पॉन्स का ड्रिफ्ट छूट जाता है एरर रिस्पॉन्स के लिए भी स्कीमा और फ़िक्सचर
बिना समीक्षा जनरेटर से बने स्कीमा एक उदाहरण से निकला required असली डेटा अस्वीकार करता है; टीम वैलिडेशन बंद कर देती है जनरेशन के बाद required/enum की मानवीय समीक्षा
सब वर्ज़न के लिए एक ग्लोबल स्कीमा v1/v2 के अंतर मिट जाते हैं स्कीमा को एंडपॉइंट के साथ वर्ज़न करें
फेल होने पर पूरा payload लॉग में PII और सीक्रेट CI लॉग में रिसाव सिर्फ JSON Pointer पथ और छोटे किए क्लिप लिखें
जमी हुई enum प्रोडक्ट में नाम बदलाव (canceled→cancelled) वैलिडेशन गिरा देता है enum बदलाव कॉन्फ़ॉर्मेंस रिव्यू से गुज़रें
वैलिडेटर का वर्ज़न पिन नहीं मशीनों में अलग व्यवहार CI में वैलिडेटर का वर्ज़न पिन करें
ऑथराइज़ेशन टेस्ट की जगह 403 एरर स्कीमा से मिल सकता है और रोल लॉजिक गलत ही रहता है स्कीमा संरचना का पहरेदार; ऑथराइज़ेशन अलग टेस्ट हो
format की सिमेंटिक्स भूलना जंक ईमेल खुले गुज़रते हैं ajv-formats लगाएँ / format_checker दें, नेगेटिव जोड़ें

8. लागू करने की चेकलिस्ट

  1. स्कीमा फ़ाइलें वर्ज़न कंट्रोल में, node scripts/lint-schemas.mjs lint के भीतर;
  2. हर स्कीमा के लिए हर बाध्यक्रम पर कम से कम 1 पॉज़िटिव और 1 नेगेटिव फ़िक्सचर; फ़ाइल का नाम ही असर्शन;
  3. एरर ऑब्जेक्ट स्नैपशॉट में; CI में --ci दें ताकि स्नैपशॉट अकेले न बहे;
  4. वैलिडेटर इंस्टेंस मॉड्यूल-स्तर पर पुनःउपयोग हो — हैंडलर में new न करें;
  5. तोड़ने वाले बदलावों का diff PR पर लटके; required/enum बदलाव अनिवार्य रिव्यू के साथ;
  6. पहला PR जानबूझकर साबित करे कि गेट गिर सकता है: एक required हटाएँ, लाल देखें, वापस लाएँ।

वैलिडेशन की कठोरता का प्रश्न (additionalProperties कब बंद करें) दूसरा विषय है — देखें additionalProperties खुली छोड़ना: स्कीमा वैलिडेशन बेकार। यह लेख इस बारे में है कि लिखे गए नियमों पर हमेशा भरोसा किया जा सके। बुनियादी बातों के लिए JSON Schema शून्य से इंजीनियरिंग तक से शुरू करें।

FAQ

स्कीमा वैलिडेशन का Pact-शैली कॉन्ट्रैक्ट टेस्टिंग से क्या संबंध है?

यह काम का बँटवारा है। स्कीमा वैलिडेशन जवाब देता है "क्या यह दस्तावेज़ संरचनात्मक रूप से सही है" — एकतरफ़ा असर्शन। उपभोक्ता-संचालित कॉन्ट्रैक्ट टेस्टिंग (Pact) जवाब देती है "क्या प्रदाता उपभोक्ता X की विशिष्ट ज़रूरतें अब भी पूरी करता है" और दो-तरफ़ा सत्यापन माँगती है। छोटी टीम स्कीमा गेट से शुरू करती है, और सेवाओं की संख्या बढ़ने पर Pact अपनाती है।

कितने नेगेटिव टेस्ट काफ़ी हैं?

हर बाध्यक्रम पर एक नेगेटिव फ़्लोर है: स्कीमा के बाध्यक्रमों की संख्या के बराबर नेगेटिव फ़िक्सचर (required, type, minimum, enum, pattern, additionalProperties…), साथ में अस्वीकृत/स्वीकृत सीमा-मान जोड़े। बाध्यक्रमों से कम नेगेटिव का मतलब: एक बाध्यक्रम बिना पहरे का है।

कौन से स्कीमा बदलाव "तोड़ने वाले" गिने जाते हैं?

उपभोक्ता के नज़रिए से मूल्य: रिस्पॉन्स से फ़ील्ड हटाना, enum हटाना या सिकोड़ना, pattern कठोर करना, रिक्वेस्ट में आवश्यक फ़ील्ड जोड़ना — सब तोड़ते हैं; रिस्पॉन्स में वैकल्पिक फ़ील्ड जोड़ना आमतौर पर सुरक्षित — बशर्ते उपभोक्ता का डिकोडर अजनबी कुंजियाँ सहता हो। PR पर json-schema-diff-validator से तुलना ऑटोमेट करें, और धूसर क्षेत्र का फ़ैसला आप कीजिए।

असली API पर टेस्ट करें या मॉक पर?

दोनों, परतों में: फ़िक्सचर टेस्ट नेटवर्क को छूते नहीं, मिलीसेकंड में चलते हैं, हर PR पर चलते हैं; असली वैलिडेशन staging पर प्रहार करता है, सेकंड खर्च करता है, स्मोक पाइपलाइन का हिस्सा है, और बिना क्रेडेंशियल skipif से टल जाता है। जो टीम CI में सिर्फ असली API चलाती है, उसे अस्थिर नेटवर्क और रेट-लिमिट घुटते हैं, और अंत में वह टेस्ट बंद कर देती है।

संदर्भ

संबंधित लेख

जब JSON Schema Errors समझ में न आएँ: Ajv Error Location और Message Governance

Validator machine errors देता है; उपयोगकर्ता जानना चाहता है कि कौन-सा input box गलत है। इसमें Ajv errors array, instancePath बनाम schemaPath, grouping और wording शामिल हैं।

उन्नत~10 मिनट पढ़ें2026-10-31 को अपडेट किया गया

additionalProperties से ऑब्जेक्ट बंद न करें तो Schema वैलिडेशन बेमानी है

वैलिडेशन पास होना साफ़ डेटा की गारंटी नहीं है। Pydantic डिफ़ॉल्ट रूप से अनजान फ़ील्ड को नज़रअंदाज़ करता है, Zod उन्हें हटा देता है, Ajv छोड़ देता है — झूठे पैरामीटर और टाइपो कुंजियाँ चुपचाप निकल जाती हैं। यह लेख मौन स्वीकृति के तीन रूप दिखाता है और पूरे तरीके देता है: ऑब्जेक्ट बंद करना, कोअर्सन नियंत्रित करना, हर नेस्टिंग स्तर कसना।

Intermediate~8 मिनट पढ़ें2026-11-02 को अपडेट किया गया

JSON Schema: मूल बातों से प्रोडक्शन तक

Keywords बहुत हैं, पर रोज़ केवल एक दर्जन इस्तेमाल होते हैं। type/properties/required/$ref सही कर लें, error messages अच्छे संभालें, और आप असली मामलों का अस्सी प्रतिशत कवर कर लेंगे।

मध्यम~14 मिनट पढ़ें2026-10-30 को अपडेट किया गया