How to Test JSON Schema with AJV in TypeScript

Moon
Moon
Oct 24, 2019 · 5 min read

How can you test if your request and response match the interfaces? When I was a beginner to Jest , I only used toMatchSnapshot . With toMatchShot , you can leave the snapshot of data you pass into expect if there is no existing snapshot. If there is, then Jest automatically compares the existing one to the value you passed into expect .

Of course, snapshot testing is awesome especially since your UI components don’t change unexpectedly. However, if the data you deal with is changed often, you can’t guarantee that it always satisfies your test cases.

TL;DR

  • Testing API response is not easy since its values are always changed. So, instead of comparing values, we can check the types of values.
  • typescript-json-schema is for generating JSON schema objects.
  • ajv is for validating your data by comparing to JSON schema object you made with typescript-json-schema

JSON Schema would be one of the solutions for you

JSON Schema is a vocabulary that allows you to annotate and validate JSON documents.

JSON Schema is a document for JSON based format data. It’s easy to maintain, read and use.

Let’s imagine there is a JSON object you own.

{
"name": "john",
"age": 30
}

This JSON object is converted as below when it passes through the JSON Schema generator.

{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/root.json",
"type": "object",
"title": "The Root Schema",
"required": [
"name",
"age"
],
"properties": {
"name": {
"$id": "#/properties/name",
"type": "string",
"title": "The Name Schema",
"default": "",
"examples": [
"john"
],
"pattern": "^(.*)$"
},
"age": {
"$id": "#/properties/age",
"type": "integer",
"title": "The Age Schema",
"default": 0,
"examples": [
30
]
}
}
}

Yes, right. Some of you might have noticed it looks kind of like . Take a little bit of time to look at that structure. What can you see? There are few properties in the object.

What we are going to focus on is properties. It contains all of the keys in your original object, including name and age. type describes what type the property should meet. Note that this is very important to know when it comes to validating JSON Schema that we are going to explore.

Okay, I got what JSON Schema is. “Then how can I convert my typescript interface to JSON Schema?”

Okay. There is a really useful library that converts typescript interfaces to JSON schema files. .

I recommend you to read first, which talks about Jest.extend . This post is under the premise, that you already know how to make custom matchers in Jest .


Step 1: Create the project structure

Let’s create the project first.

> npm init -y
> mkdir src

Then, install the needed packages.

> npm i -D jest ts-jest typescript @types/jest
> touch jest.config.js
> touch ./src/tsconfig.json

tsconfig.json file should contain these options.

And here’s for jest.config.js

Create the test and typing folders.

> mkdir ./src/typings ./src/__tests__
> touch ./src/typings/index.d.ts

What we will do now is to create the typed file for typescript, so we can use it later in the test suite. Write that code in index.d.ts .

To let Jest know we are ready to register our new matcher, create the new folder and make a file. We will import this file in the test file and invoke the function before all test cases.

> mkdir ./src/hooks
> touch ./src/hooks/index.ts

extendJSCMatcher isn’t completed yet, we will complete that function later.

And lastly, create the test file and the fundamental preparation is done.

> touch ./src/__tests__/jsc.spec.ts

Step 2: Create interfaces and packages

If you follow the steps well so far, the project should be like below.

node_modules
src
__tests__
jsc.spec.ts
hooks
index.ts
typings
index.d.ts
jest.config.js
package-lock.json
package.json

Since we are going to use node native modules(path and fs), we need to install some type of definition package. Additionally, here are our heroes, JSON schema validator and JSON schema generator packages.

> npm i -D @types/node typescript-json-schema ajv

As long as you use typescript, you should install @types/node package to use node native modules such as path or fs. .

typescript-json-schema is the amazing library for generating your typescript interfaces to JSON schema files. Even though the guide isn’t that nice, you should be okay to write a simple example.

ajv is the fastest JSON schema validator for Node.js and browser. What it does is very opposite to whattypescript-json-schema does. ajv checks if a given object is written correctly, based on the given schema. If the object doesn’t match the schema, it returns an error object.

Now we need to create the folder for interfaces.

> mkdir ./src/models
> touch ./src/models/ISchool.ts

There will be 3 payloads in the interface file.

Step 3: Write JSON Schema generating code

Make the folder hierarchy of utils like below.

utils
index.ts
jsc.ts
validate.ts

First things first, let’s start with index.ts

It’s a simple pipeline function, but if you don’t understand this or don’t know what pipe function is, I recommend you to . Note that there is no return value there. We will just invoke that function in a different file.

Now, let’s talk about the package, typescript-json-schema. There are a few steps you need to remember.

1. import the package
2. generate the object called program
3. generate the object called generator, using program

The whole picture seems quite simple. And there are only a couple of parameters to pass for #2 and #3.

There are few methods in generator . One of them is getUserSymbols, which gets all of the symbols. A lot of symbols are in the list, as you could see if you run console.dir(generator.getUserSymbols()), look at the very top 3 items.

// all symbols
[ 'StudentInterface',
'TeacherInterface',
'SchoolInterface',
'BaseComment',
'CommentBlock',
...
]

They are the typescript interfaces you wrote earlier. It’s because you added additional files into the program generator function.

console.log(files);
// ['..yourPath/ISchoolts']

This time, try console.log(generator.getSchemaForSymbol(‘StudentInterface’)) and see what comes after.

// this is the schema object
{ type: 'object',
properties:
{ name: { type: 'string' },
age: { type: 'number' },
address: { type: 'string' } },
required: [ 'age', 'name' ],
'$schema': '
}

Now you’ve made schema objects!

Finally, you can store these schema objects in the directory, schema.

step 4: Validate JSON Schema with ajv

This will be the last step. Since we made JSON Schema files in the schema folder, it’s time to use ajv.

Open validate.ts file and write this code.

There are a few more ways to use ajv, When validate finds no errors, then the error message from errorText() is “No Errors”.

Run this file and see what result you would get.

> npx ts-node ./src/utils/validate.ts// result
> data should have required property 'subject'

Try with other testing objects and see the result!

And we forgot to complete the jest hook file that we made. It’s literally for the test code, so if you don’t want to run tests, you can skip this.

Full Code

I uploaded the full codes with a little bit of refactoring.

JavaScript in Plain English

Learn the web's most important programming language.

Moon

Written by

Moon

Front End Web Developer in S.Korea. Interested in writing about Programmings. All of the posts could be written in English and Korean, which is my mother tongue

JavaScript in Plain English

Learn the web's most important programming language.

More From Medium

More from JavaScript in Plain English

More from JavaScript in Plain English

More from JavaScript in Plain English

32 funny Code Comments that people actually wrote

10.4K

More from JavaScript in Plain English

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade