Typescript/Jest 从服务模拟共享函数
Typescript/Jest mocking a shared function from a service
我有一个 NestJS 服务,但这并不是真正的 NestJS 问题。但是,NestJS 测试正在进行中,我用它来创建测试服务等。我的服务用于进行多次调用,return 所有数据。
在我的场景中,有一个服务 (myservice) 将获得一些结果(数据对象),这些结果结合了来自各种来源的结果。外部源都是基于Promise(async/await)的,用来获取所有不同的数据点,把这个结合到myservice(别人扩展的一个基础服务)中结合错误处理,promise结果处理等
我在尝试模拟外部调用以进行测试时遇到问题。我的问题是关于从 NodeJS 读取数据的函数。我可以毫无问题地自行测试函数,但现在我正试图在我的服务中模拟这个函数调用。
问题中函数的简短示例,作为以下部分的一部分:
myservice.ts
public async GetData(DataType: number = DATA_TYPES.TEXT): Promise<ResultFile> {
const ResultData: ResultFile = new ResultFile(DataType);
return new Promise<ResultFile>((resolve, reject) => {
const Data$: Promise<string> = this.GetResultFileContents();
const StatusCode$: Promise<number> = this.GetResultFileStatusCode();
Promise.all([Data$, StatusCode$])
.then((Results) => {
ResultData.Contents = Results[0]; // Promise returns a string
ResultData.StatusCode = Results[1]; // Promise returns a number
resolve(ResultData);
})
.catch((Exception: Error) => {
ResultData.StatusCode = HttpStatus.NOT_FOUND;
ResultData.Contents = Exception.message;
reject(ResultData);
});
});
}
以上是尝试检索不同数据的主要方法。这调用的 promises 比那里的 2 个更多,但有两个会显示我的问题。
public async GetResultFileContents(): Promise<string> {
try {
const Results$: string = await ReadFileContents(this.Directory_, this.BaseName_);
return Results$;
} catch (Exception) {
throw new HttpException(`File not found: ${this.BaseName_} in ${this.Directory_}`, HttpStatus.NOT_FOUND);
}
}
public async GetResultFileStatusCode(): Promise<number> {
let StatusCode: number = 0;
try {
const StatusCodeFile: string = `${this.BaseName_}.err`;
const StatusCodeData$ = await ReadFileContents(this.Directory_, StatusCodeFile);
StatusCode = GetIntegerFromText(StatusCodeData$);
} catch (Exception) {
StatusCode = HttpStatus.OK; // Return OK since a specific status was not set to be returned.
}
return new Promise<number>((resolve) => {
resolve(StatusCode);
});
}
被调用的两个方法,return 承诺,都使用外部函数 ReadFileContents()。这是我想要模拟的函数,因为它可以 return 数据作为字符串,或者抛出异常,包装 OS 检查(除其他外)包含数据的文件.
这个函数是通用的,并且被许多从文件系统读取数据的方法共享。还有一些类似于 REST 调用的东西,也有同样的问题,但这是一个简单的例子。
我的问题现在出现在测试文件中。虽然我可以测试服务和 myservice.ts
中的方法,但我不知道如何模拟对 ReadFileContents() 的外部调用以确保 myservice.ts
中的方法正确执行。
我想测试不同的return字符串,以及文件不存在时的异常捕获等等
我的测试:
import { Test, TestingModule } from '@nestjs/testing';
import { MyService } from './my.service';
// TODO: Need to mock the internal calls to ReadFileContents() to throw exceptions
describe('MyService (mock)', () => {
let service: MyService;
afterEach(() => {});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MyService],
}).compile();
service = module.get<MyService>(MyService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should handle exceptions when reading missing .hdr file', async () => {
// const mockReadFileContents = jest.spyOn(service.???);
});
});
在最后一个函数中,我不知道如何模拟 ReadFileContents,因为它只是服务中的一个函数,位于另一个源文件中。
我真的不想在服务中创建一个 public 方法来简单地调用这个函数,所以我可以模拟它,如果我能帮忙的话。
请忽略缺失的功能或其他拼写错误,因为这是一个快速编辑,以提供我正在尝试完成的示例。
我一直在努力解决这个问题,但无法让模拟工作。然后我确实更改了我试图模拟的功能,使其位于另一个服务 (fsService) 中,并通过具有依赖注入的构造函数在 myService 中使用该服务。
constructor(
private FileSystem_: fsService,
) {}
然后可以通过模拟服务、提供虚假定义({provide: fsService, useClass: fsServiceMock })等方式在测试中轻松模拟。
import { Test, TestingModule } from '@nestjs/testing';
import { MyService } from './my.service';
import { FsService } from './fs.service';
describe('MyService (mock)', () => {
let service: MyService;
let FSService_: FsService;
afterEach(() => {
jest.resetAllMocks();
});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
FSService,
MyService,
],
}).compile();
FsService_ = module.get<FSService>(FSService);
service = module.get<MyService>(MyService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should handle exceptions when reading missing .hdr file', async () => {
FSService_.ReadFileContents = jest.fn().mockRejectedValue(new Error('ENOENT: File not Found'));
...
});
});
我有一个 NestJS 服务,但这并不是真正的 NestJS 问题。但是,NestJS 测试正在进行中,我用它来创建测试服务等。我的服务用于进行多次调用,return 所有数据。
在我的场景中,有一个服务 (myservice) 将获得一些结果(数据对象),这些结果结合了来自各种来源的结果。外部源都是基于Promise(async/await)的,用来获取所有不同的数据点,把这个结合到myservice(别人扩展的一个基础服务)中结合错误处理,promise结果处理等
我在尝试模拟外部调用以进行测试时遇到问题。我的问题是关于从 NodeJS 读取数据的函数。我可以毫无问题地自行测试函数,但现在我正试图在我的服务中模拟这个函数调用。
问题中函数的简短示例,作为以下部分的一部分: myservice.ts
public async GetData(DataType: number = DATA_TYPES.TEXT): Promise<ResultFile> {
const ResultData: ResultFile = new ResultFile(DataType);
return new Promise<ResultFile>((resolve, reject) => {
const Data$: Promise<string> = this.GetResultFileContents();
const StatusCode$: Promise<number> = this.GetResultFileStatusCode();
Promise.all([Data$, StatusCode$])
.then((Results) => {
ResultData.Contents = Results[0]; // Promise returns a string
ResultData.StatusCode = Results[1]; // Promise returns a number
resolve(ResultData);
})
.catch((Exception: Error) => {
ResultData.StatusCode = HttpStatus.NOT_FOUND;
ResultData.Contents = Exception.message;
reject(ResultData);
});
});
}
以上是尝试检索不同数据的主要方法。这调用的 promises 比那里的 2 个更多,但有两个会显示我的问题。
public async GetResultFileContents(): Promise<string> {
try {
const Results$: string = await ReadFileContents(this.Directory_, this.BaseName_);
return Results$;
} catch (Exception) {
throw new HttpException(`File not found: ${this.BaseName_} in ${this.Directory_}`, HttpStatus.NOT_FOUND);
}
}
public async GetResultFileStatusCode(): Promise<number> {
let StatusCode: number = 0;
try {
const StatusCodeFile: string = `${this.BaseName_}.err`;
const StatusCodeData$ = await ReadFileContents(this.Directory_, StatusCodeFile);
StatusCode = GetIntegerFromText(StatusCodeData$);
} catch (Exception) {
StatusCode = HttpStatus.OK; // Return OK since a specific status was not set to be returned.
}
return new Promise<number>((resolve) => {
resolve(StatusCode);
});
}
被调用的两个方法,return 承诺,都使用外部函数 ReadFileContents()。这是我想要模拟的函数,因为它可以 return 数据作为字符串,或者抛出异常,包装 OS 检查(除其他外)包含数据的文件.
这个函数是通用的,并且被许多从文件系统读取数据的方法共享。还有一些类似于 REST 调用的东西,也有同样的问题,但这是一个简单的例子。
我的问题现在出现在测试文件中。虽然我可以测试服务和 myservice.ts
中的方法,但我不知道如何模拟对 ReadFileContents() 的外部调用以确保 myservice.ts
中的方法正确执行。
我想测试不同的return字符串,以及文件不存在时的异常捕获等等
我的测试:
import { Test, TestingModule } from '@nestjs/testing';
import { MyService } from './my.service';
// TODO: Need to mock the internal calls to ReadFileContents() to throw exceptions
describe('MyService (mock)', () => {
let service: MyService;
afterEach(() => {});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MyService],
}).compile();
service = module.get<MyService>(MyService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should handle exceptions when reading missing .hdr file', async () => {
// const mockReadFileContents = jest.spyOn(service.???);
});
});
在最后一个函数中,我不知道如何模拟 ReadFileContents,因为它只是服务中的一个函数,位于另一个源文件中。
我真的不想在服务中创建一个 public 方法来简单地调用这个函数,所以我可以模拟它,如果我能帮忙的话。
请忽略缺失的功能或其他拼写错误,因为这是一个快速编辑,以提供我正在尝试完成的示例。
我一直在努力解决这个问题,但无法让模拟工作。然后我确实更改了我试图模拟的功能,使其位于另一个服务 (fsService) 中,并通过具有依赖注入的构造函数在 myService 中使用该服务。
constructor(
private FileSystem_: fsService,
) {}
然后可以通过模拟服务、提供虚假定义({provide: fsService, useClass: fsServiceMock })等方式在测试中轻松模拟。
import { Test, TestingModule } from '@nestjs/testing';
import { MyService } from './my.service';
import { FsService } from './fs.service';
describe('MyService (mock)', () => {
let service: MyService;
let FSService_: FsService;
afterEach(() => {
jest.resetAllMocks();
});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
FSService,
MyService,
],
}).compile();
FsService_ = module.get<FSService>(FSService);
service = module.get<MyService>(MyService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should handle exceptions when reading missing .hdr file', async () => {
FSService_.ReadFileContents = jest.fn().mockRejectedValue(new Error('ENOENT: File not Found'));
...
});
});