Å mocke Express Request med Jest og Typescript ved bruk av riktige typer
Jeg har hatt litt problemer med å få den riktige Express Request-typen til å fungere i Jest. Jeg har en enkel brukerregistrering som fungerer med denne koden:
import { userRegister } from '../../controllers/user';
import { Request, Response, NextFunction } from 'express';
describe('User Registration', () => {
test('User has an invalid first name', async () => {
const mockRequest: any = {
body: {
firstName: 'J',
lastName: 'Doe',
email: '[email ',
password: 'Abcd1234',
passwordConfirm: 'Abcd1234',
company: 'ABC Inc.',
},
};
const mockResponse: any = {
json: jest.fn(),
status: jest.fn(),
};
const mockNext: NextFunction = jest.fn();
await userRegister(mockRequest, mockResponse, mockNext);
expect(mockNext).toHaveBeenCalledTimes(1);
expect(mockNext).toHaveBeenCalledWith(
new Error('First name must be between 2 and 50 characters')
);
});
});
Men, hvis jeg endrer:
const mockRequest: any = {
body: {
firstName: 'J',
lastName: 'Doe',
email: '[email ',
password: 'Abcd1234',
passwordConfirm: 'Abcd1234',
company: 'ABC Inc.',
},
};
til :
const mockRequest: Partial<Request> = {
body: {
firstName: 'J',
lastName: 'Doe',
email: '[email ',
password: 'Abcd1234',
passwordConfirm: 'Abcd1234',
company: 'ABC Inc.',
},
};
Fra TypeScript-dokumentasjonen (https://www.typescriptlang.org/docs/handbook/utility-types.html#partialt) bør dette gjøre alle feltene på Request-objektet valgfrie.
Men jeg får denne feilen:
Argument of type 'Partial<Request>' is not assignable to parameter of type 'Request'.
Property '[Symbol.asyncIterator]' is missing in type 'Partial<Request>' but required in type 'Request'.ts(2345)
stream.d.ts(101, 13): '[Symbol.asyncIterator]' is declared here.
Jeg håpet at noen med litt mer TypeScript-erfaring kunne kommentere og la meg vite hva jeg gjør galt.
Svar #1
Din mock-datatype trenger ikke å passe perfekt med de faktiske dataene. Vel, det gjør den ikke per definisjon. Det er bare en mock, ikke sant?
Det du trenger er en typepåstand. Det er en måte å fortelle TypeScript «Greit kompis, jeg vet hva jeg gjør her.».
Dette er ikke produksjonskode, det er en test. Du kjører det sannsynligvis til og med i overvåkingsmodus. Vi kan avvise noe typesikkerhet her uten problem. TypeScript vet ikke at det er en mock, men det gjør vi.
const mockRequest = {
body: {
firstName: 'J',
lastName: 'Doe',
email: '[email ',
password: 'Abcd1234',
passwordConfirm: 'Abcd1234',
company: 'ABC Inc.',
},
} as Request;
Hvis noe krasjer under testen, fordi mockRequest ikke er lik nok Request, vil vi vite det og vi vil fikse mocken, legge til noen nye egenskaper osv.
Hvis as Request ikke fungerer, kan du fortelle TypeScript «JEG VET VIRKELIG hva jeg gjør her» ved først å anta any eller unknown og deretter til typen du trenger. Det vil se slik ut
const x: number = "not a number :wink:" as any as number;
Det er nyttig når vi ønsker å teste at koden vår ikke fungerer godt med dårlig input.
For ditt spesielle tilfelle -- å lage en mock av express Request -- finnes det jest-express som kan hjelpe deg, hvis du selvfølgelig har plass til node_modules-størrelsen.
Svar #2
For fremtidig søk om dette temaet, anbefaler jeg å se på dette biblioteket: https://www.npmjs.com/package/node-mocks-http
Dette biblioteket har metoder for å lage mockede objekter for Request og Response i Express-rammeverket, noe som hjalp meg mye og var den enkle måten jeg fant.
Enkel enhetstest eksempel:
import { Request, Response } from 'express';
import {
createRequest, createResponse, MockRequest, MockResponse,
} from 'node-mocks-http';
import { AppController } from './app-controller';
import { APP_NAME, APP_VERSION } from '../../constants';
describe('AppController - UnitTestCase', () => {
let controller: AppController;
let request: MockRequest<Request>;
let response: MockResponse<Response>;
beforeEach(() => {
controller = new AppController();
/** Response Mock */
response = createResponse();
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
describe('GET /', () => {
it('should return 200 and API Name + API Version', (done) => {
/** Request Mock */
request = createRequest({
method: 'GET',
url: '/',
});
AppController.index(request, response);
const body = { app: `${APP_NAME}:${APP_VERSION}` };
const result = response._getJSONData();
expect(result).toMatchObject(body);
expect(result.app).toEqual(body.app);
expect(response.getHeaders()).toHaveProperty('content-type');
console.log('headers', response.getHeaders());
console.log('response body', result);
done();
});
});
});
