Outline
Automatic E2E (End-to-End) test functions generator for NestJS
I made automatic e2e test functions generator for NestJS, corresponding to every API endpoints. Just setup nestia following the guide documents, and run npx nestia e2e
comnand, then you can get the automatically generated e2e functions.
Also, currently nestia supports automatic SDK and e2e test functions generator only for NestJS. However, at next month, every langagues and every frameworks would be supported.
- Guide Documents: https://nestia.io/docs/sdk/e2e/
- Github Repository: https://github.com/samchon/nestia
import typia, { Primitive } from "typia";
import api from "../../../../src/api";
import type { IBbsArticle } from "../../../../src/api/structures/IBbsArticle";
export const test_api_bbs_articles_store = async (
connection: api.IConnection,
): Promise<void> => {
const output: Primitive<IBbsArticle> =
await api.functional.bbs.articles.store(
connection,
typia.random<Primitive<string>>(),
typia.random<Primitive<IBbsArticle.IStore>>(),
);
typia.assert(output);
};
Principles
The secret of automatic e2e
test functions generation is, just analyzing your NestJS backend server codes in the compilation level.
When such NestJS backend server code comes:
import { Controller } from "@nestjs/common";
import typia from "typia";
import core from "@nestia/core";
import { IBbsArticle } from "@api/lib/structures/IBbsArticle";
@Controller("/bbs/articles/:section")
export class BbsArticlesController {
/**
* Store a new article.
*
* @param section Section code
* @param input Content to store
* @returns Newly archived article
*/
@core.TypedRoute.Post()
public async store(
@core.TypedParam("section") section: string,
@core.TypedBody() input: IBbsArticle.IStore,
): Promise<IBbsArticle> {
return {
...typia.random<IBbsArticle>(),
section,
...input,
};
}
/**
* Update an article.
*
* @param section Section code
* @param id Target article ID
* @param input Content to update
* @returns Updated content
*/
@core.TypedRoute.Put(":id")
public async update(
@core.TypedParam("section") section: string,
@core.TypedParam("id", "uuid") id: string,
@core.TypedBody() input: IBbsArticle.IStore,
): Promise<IBbsArticle> {
return {
...typia.random<IBbsArticle>(),
id,
section,
...input,
};
}
}
Nestia analyzes above code, and generates an SDK library.
The SDK (Software Development Kit) library is composed with a set of fetch
functions interacting with the NestJS server. It makes client (maybe frontend) developers to easily interact with the backend server by auto-completion and type safety.
/**
* @packageDocumentation
* @module api.functional.bbs.articles
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
//================================================================
import { Fetcher, Primitive } from "@nestia/fetcher";
import type { IConnection } from "@nestia/fetcher";
import type { IBbsArticle } from "./../../../structures/IBbsArticle";
/**
* Store a new article.
*
* @param connection connection Information of the remote HTTP(s) server with headers (+encryption password)
* @param section Section code
* @param input Content to store
* @returns Newly archived article
*
* @controller BbsArticlesController.store()
* @path POST /bbs/articles/:section
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
export function store
(
connection: IConnection,
section: string,
input: Primitive<store.Input>
): Promise<store.Output>
{
return Fetcher.fetch
(
connection,
store.ENCRYPTED,
store.METHOD,
store.path(section),
input
);
}
export namespace store
{
export type Input = Primitive<IBbsArticle.IStore>;
export type Output = Primitive<IBbsArticle>;
export const METHOD = "POST" as const;
export const PATH: string = "/bbs/articles/:section";
export const ENCRYPTED: Fetcher.IEncrypted = {
request: false,
response: false,
};
export function path(section: string): string
{
return `/bbs/articles/${encodeURIComponent(section ?? "null")}`;
}
}
/**
* Update an article.
*
* @param connection connection Information of the remote HTTP(s) server with headers (+encryption password)
* @param section Section code
* @param id Target article ID
* @param input Content to update
* @returns Updated content
*
* @controller BbsArticlesController.update()
* @path PUT /bbs/articles/:section/:id
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
export function update
(
connection: IConnection,
section: string,
id: string,
input: Primitive<update.Input>
): Promise<update.Output>
{
return Fetcher.fetch
(
connection,
update.ENCRYPTED,
update.METHOD,
update.path(section, id),
input
);
}
export namespace update
{
export type Input = Primitive<IBbsArticle.IStore>;
export type Output = Primitive<IBbsArticle>;
export const METHOD = "PUT" as const;
export const PATH: string = "/bbs/articles/:section/:id";
export const ENCRYPTED: Fetcher.IEncrypted = {
request: false,
response: false,
};
export function path(section: string, id: string): string
{
return `/bbs/articles/${encodeURIComponent(section ?? "null")}/${encodeURIComponent(id ?? "null")}`;
}
}
At last, nestia generates E2E test functions utilizing the SDK library.
For reference, automatically generated e2e functions compose parameter values through typia.random<T>()
function like example below. Therefore, you need to customize those E2E test functions, to be suitable for your domain logics.
import typia, { Primitive } from "typia";
import api from "./../../../../api";
import type { IBbsArticle } from "./../../../../api/structures/IBbsArticle";
export const test_api_bbs_articles_store = async (
connection: api.IConnection
): Promise<void> => {
const output: Primitive<IBbsArticle> =
await api.functional.bbs.articles.store(
connection,
typia.random<Primitive<string>>(),
typia.random<Primitive<IBbsArticle.IStore>>(),
);
typia.assert(output);
};
export const test_api_bbs_articles_update = async (
connection: api.IConnection
): Promise<void> => {
const output: Primitive<IBbsArticle> =
await api.functional.bbs.articles.update(
connection,
typia.random<Primitive<string>>(),
uuid(),
typia.random<Primitive<IBbsArticle.IStore>>(),
);
typia.assert(output);
};
const uuid = (): string =>
"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
What nestia is?
A set of helper libraries for NestJS.
-
@nestia/core
: super-fast decorators -
@nestia/sdk
:- SDK generator for clients
- Swagger generator evolved than ever
- Automatic E2E test functions generator
-
nestia
: just CLI (command line interface) tool
Note
- Only one line required, with pure TypeScript type
- Runtime validator is 20,000x faster than
class-validator
- JSON serialization is 200x faster than
class-transformer
- SDK is similar with tRPC, but much advanced
Left is server code, and right is e2e test function utilizing SDK
Nestia is one of my open source library which can boost up NestJS performance and productivity. One thing interesting is, all of those benefits are coming from a point: nestia supports pure TypeScript type.
In normal case, NestJS forces user to define triple redundant DTO schemas. The 1st is pure TypeScript type, the 2nd is class-validator
decorator, and the last (3rd) is @nestjs/swagger
decorator.
You may understand how it annoying and time-consuming, even you haven't used NestJS at all, just by looking below code.
export class BbsArticle {
@IsString()
@ApiProperty({
format: "uuid",
})
id!: string;
// DUPLICATED SCHEMA DEFINITION
// - duplicated function call + property type
// - have to specify `isArray` and `nullable` props by yourself
@IsArray()
@IsObject()
@ValidateNested()
@Type(() => AttachmentFile)
@ApiProperty({
type: () => AttachmentFile,
nullable: true,
isArray: true,
description: "List of attached files.",
})
files!: AttachmentFile[] | null;
@IsString()
@IsOptional()
@ApiProperty({
type: "string",
nullable: true,
minLength: 5,
maxLength: 100,
description: "Title of the article.",
})
title!: string | null;
@IsString()
@ApiProperty({
description: "Main content body of the article."
})
body!: string;
@IsString()
@ApiProperty({
format: "date-time",
description: "Creation time of article",
})
created_at!: string;
}
export class AttachmentFile {
@IsString()
@IsOptional()
@ApiProperty({
type: "string",
nullable: true,
maxLength: 255,
pattern: "^[a-zA-Z0-9-_]+$",
description: "File name.",
})
name!: string | null;
@IsString()
@IsOptional()
@ApiProperty({
type: "string",
nullable: true,
maxLength: 255,
pattern: "^[a-zA-Z0-9-_]+$",
description: "File extension.",
})
extension!: string | null;
@IsString()
@ApiProperty({
format: "url",
description: "URL of the file.",
})
url!: string;
}
Besides, nestia needs only pure TypeScript type.
If you're wondering how nestia can do that, please read my previous article - [Nestia] Boost up NestJS server much faster and easier (maximum 20,000x faster + tRPC similar).
export interface IBbsArticle {
/**
* Primary Key.
*
* @format uuid
*/
id: string;
/**
* List of attached files.
*/
files: IAttachmentFile[] | null;
/**
* Title of the article.
*
* @minLength 5
* @maxLength 100
*/
title: string | null;
/**
* Main content body of the article.
*/
body: string;
/**
* Creation time of article.
*
* @format date-time
*/
created_at: string;
}
export interface IAttachmentFile {
/**
* File name.
*
* @pattern ^[a-z0-9]+$
* @maxLength 255
*/
name: string | null;
/**
* File extension.
*
* @pattern ^[a-z0-9]+$
* @maxLength 8
*/
extension: string | null;
/**
* URL of the file.
*
* @format uri
*/
url: string;
}
Someone said me that this is the most important benefit of nestia, even rather than boosting up performance enourmously. He said that, it makes nestia to be "no reason not to use when developing NestJS".
I really like his word "no reason not to use". Do you agree?
Next Episode
In nowadays, I'm developing migration project from Swagger to NestJS.
If the project being done, you can create a NestJS project from any swagger.json
file. Therefore, what language and framework ever you use, you also can automatically generate SDK library and E2E test functions for every API endpoints.
Be looking forward to it. I'll come back with at next month.
Also preparing automatic frontend application generator from NestJS server code. Its name is
reactia
, and may possible to release in this year.In means that, with the Swagger to NestJS migration project, you can easily create a full-stack application just by taking a
swagger.json
file.Be looking forward to them. I'll show you a new world.
Top comments (4)
Your post are increidble, but I am noob on Nest Js, Do you have a chanel where to you show how useall these improvements?
Maybe you're meaning performance.
Read this issue and follow it, then you can run benchmark on your computer:
github.com/samchon/typia/issues/337
Thanks for your answer, but I don't talk just about performance, I talk about video explanation.
Then visit website, and follow its explanation.
nestia.io/docs