#typescript #unit-testing #mocha.js #sinon
#typescript #модульное тестирование #mocha.js #sinon
Вопрос:
В моей архитектуре rest есть контроллер (обрабатывает http-запрос) и сервис (бизнес-логика для предоставления данных).
Для тестирования контроллера я пытаюсь заглушить службу, чтобы обеспечить фиксированный ответ, но я не понял, как заглушить метод, который требует пользовательского объекта в качестве аргумента (если аргумент является литералом, он работает).
С пользовательским объектом (ферма) заглушение не работает, потому что я не получаю обещание от метода service, это ошибка:
Ошибка типа: не удается прочитать свойство ‘then’ с нулевым значением в FarmsController.createFarm (/Users/giovannimarino/Projects/rt-cloud/services/farms/src/farms/farms.controller.ts:17:17)
farms.controller.spec.ts
describe('FarmsController', () => {
const farmsServiceMock: FarmsService = mock(FarmsService);
let controller: FarmsController;
interface TestData {
farm: Farm;
}
let testData: TestData;
beforeEach(() => {
reset(farmsServiceMock);
const farmsServiceMockInstance: FarmsService = instance(farmsServiceMock);
controller = new FarmsController(farmsServiceMockInstance);
testData = {
farm: <Farm> {
name: 'CattD',
imageUrl: 'img/farm-123b341.png',
lang: 'en',
}
};
});
describe('createFarm function', () => {
describe('success', () => {
it('should return HTTP 200 OK', async () => {
when(farmsServiceMock.createFarm(testData.farm)).thenReturn(Promise.resolve<Farm>(testData.farm));
const pathParameters: PathParameter = {
name: 'CattD',
};
const bodyRequest: Body = {
name: testData.farm.name,
imageUrl: testData.farm.imageUrl,
lang: testData.farm.lang
};
const response: ApiResponseParsed<Farm> = await callSuccess<Farm>(controller.createFarm, pathParameters, bodyRequest);
expect(response.statusCode).to.equal(HttpStatusCode.Ok);
});
});
});
});
farm.controller.ts
export class FarmsController {
public constructor(private readonly _service: FarmsService) {
}
public createFarm: ApiHandler = (event: ApiEvent, context: ApiContext, callback: ApiCallback): void => {
if (!event.body) {
throw new Error('Empty input');
}
const input: Farm = <Farm> JSON.parse(event.body);
this._service.createFarm(input)
.then((data: Farm) => {
return ResponseBuilder.created(data, callback); // tslint:disable-line arrow-return-shorthand
})
.catch((error: ErrorResult) => {
if (error instanceof NotFoundResult) {
return ResponseBuilder.notFound(error.code, error.description, callback);
}
if (error instanceof ForbiddenResult) {
return ResponseBuilder.forbidden(error.code, error.description, callback);
}
return ResponseBuilder.internalServerError(error, callback);
});
}
}
farm.service.ts
export class FarmsService {
public constructor(private readonly _repo: FarmsRepository) {
}
public async createFarm(farm: Farm): Promise<Farm> {
try {
return this._repo.create(farm);
} catch (error) {
throw error;
}
}
}
callSuccess
export const callSuccess: SuccessCaller = <T>(handler: ApiHandler,
pathParameters?: PathParameter, body?: Body): Promise<ApiResponseParsed<T>> => {
// tslint:disable-next-line typedef (Well-known constructor.)
return new Promise((resolve, reject) => {
const event: ApiEvent = <ApiEvent> {};
if (pathParameters) {
event.pathParameters = pathParameters;
}
if (body) {
event.body = JSON.stringify(body);
}
handler(event, <ApiContext> {}, (error?: Error | null | string, result?: ApiResponse): void => {
if (typeof result === 'undefined') {
reject('No result was returned by the handler!');
return;
}
const parsedResult: ApiResponseParsed<T> = result as ApiResponseParsed<T>;
parsedResult.parsedBody = JSON.parse(result.body) as T;
resolve(parsedResult);
});
});
};
Комментарии:
1. Что вы используете для заглушки? Я не знаком с
when / thenReturn
синтаксисом в вашем тесте.
Ответ №1:
Я столкнулся с той же проблемой и решил ее, подделав метод с помощью deepEqual()
, передав свой пользовательский объект в качестве параметра.
import {deepEqual, instance, mock, when} from 'ts-mockito';
const myCustomObj = {
userId: 123
};
when(mockedObj.myMethod(deepEqual(myCustomObj))).thenResolve(myPromise);
Комментарии:
1. Да, по умолчанию
ts-mockito
используетсяstrictEqual
. Поэтому, если вы хотите проверить содержимое объектов, используйтеdeepEqual
, как предложил @Knnwulf2. @NagRock: это где-нибудь задокументировано? Или il может быть задокументирован здесь ? Вы бы просмотрели и приняли PR?