我正在尝试为具有 gRPC 客户端的 NestJS 微服务编写 e2e 测试。无论我如何尝试在 e2e 测试中模拟 ClientsModule,它似乎仍然无法获取配置并且无法找到 *.proto 文件。
请在下面找到我的代码示例:
app.module.ts 中的客户端模块
// src/app.module.ts
@Module({
imports: [
HttpModule,
...
...
ClientsModule.register([
{
name: 'USERS_PACKAGE',
transport: Transport.GRPC,
options: {
package: 'users',
credentials: credentials.createInsecure(),
protoPath: join(__dirname, '../proto/users.proto'),
url: 'users-grpc-server.users:50051',
},
},
]),
],
controllers: [UsersController],
providers: [UsersService, UsersClient],
})
export class AppModule {}
用户客户端对 gRPC Server NestJS 微服务进行 gRPC 调用:
// src/clients/users.client.ts
@Injectable()
export class UsersClient implements OnModuleInit {
private readonly logger: Logger = new Logger(UsersClient.name);
private readonly API_KEY: string = this.configService.get<string>('services.v1.api-key');
private usersController: usersController;
constructor(@Inject('USERS_PACKAGE') private client: ClientGrpc, private configService: ConfigService) {}
onModuleInit() {
this.usersController = this.client.getService<UsersController>('UsersController');
}
async getUsers(headers: IncomingHttpHeaders, userId: string): Promise<Users> {
let users: Users = null;
const metadata = new Metadata();
metadata.add('Authorization', headers.authorization);
metadata.add('x-api-key', this.API_KEY);
try {
users = await this.usersController.getUsers({}, metadata);
} catch (e) {
const { message, response: { status = 500 } = {}, stack } = e;
this.logger.error(stack);
throw new HttpException(message, status);
}
return users;
}
}
更新nest-cli.json
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"assets": [{"include": "../config/*.yaml", "outDir": "./dist/config"}, {"include": "**/*.proto", "outDir": "./dist"}]
}
}
e2e测试配置
// test/app.e2e-spec.json
let app: NestFastifyApplication;
let httpService: HttpService;
let cacheManager: Cache;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
HttpModule,
...
...
ClientsModule,
],
})
.overrideProvider(ClientsModule)
.useValue({
name: 'USERS_PACKAGE',
transport: Transport.GRPC,
options: {
package: 'users',
credentials: ServerCredentials.createInsecure(),
protoPath: join(__dirname, '../src/proto/users.proto'),
url: 'localhost:50051',
},
})
.compile();
app = module.createNestApplication(new FastifyAdapter());
await app.init();
await app.getHttpAdapter().getInstance().ready();
httpService = module.get<HttpService>(HttpService);
cacheManager = module.get<Cache>(CACHE_MANAGER);
});
错误:
● Test suite failed to run
The invalid .proto definition (file at "/users-service-grpc-client/app/proto/users.proto" not found)
> 26 | ClientsModule.register([
| ^
27 | {
28 | name: 'USERS_PACKAGE',
29 | transport: Transport.GRPC,
at ClientGrpcProxy.loadProto (node_modules/@nestjs/microservices/client/client-grpc.js:220:39)
at ClientGrpcProxy.createClients (node_modules/@nestjs/microservices/client/client-grpc.js:193:34)
at new ClientGrpcProxy (node_modules/@nestjs/microservices/client/client-grpc.js:29:33)
at Function.create (node_modules/@nestjs/microservices/client/client-proxy-factory.js:27:24)
at node_modules/@nestjs/microservices/module/clients.module.js:12:80
at Array.map (<anonymous>)
at Function.register (node_modules/@nestjs/microservices/module/clients.module.js:10:41)
at Object.<anonymous> (src/app.module.ts:26:19)
看起来 e2e 测试没有使用测试中的
ClientsModule
配置,并且仍在使用 app.module.ts
中的 ClientsModule 配置。
有谁知道正确配置的方法吗?
我不确定这是如何工作的,但我将 src/app.module.ts 中的 ClientsModule 更改为 registerAsync,因为我想让 url 动态化,现在它似乎工作正常
// src/app.module.ts
@Module({
imports: [
HttpModule,
...
...
ClientsModule.registerAsync([
{
name: 'USERS_PACKAGE',
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
transport: Transport.GRPC,
options: {
package: 'users',
credentials: credentials.createInsecure(),
protoPath: join(__dirname, '../proto/users.proto'),
url: configService.get<string>('services.users-grpc-server-service.url'),
},
}),
inject: [ConfigService],
},
]),
],
controllers: [UsersController],
providers: [UsersService, UsersClient],
})
export class AppModule {}
注意:在E2E测试中,我还必须使用overrideProvider来模拟grpc调用
// test/app.e2e-spec.ts
const module: TestingModule = await Test.createTestingModule({
imports: [
HttpModule,
...
...
ClientsModule,
],
})
.overrideProvider('USERS_PACKAGE')
.useValue({
getService: () => ({
getUsers: jest.fn().mockReturnValue({
"name": "test user",
"age": "55",
"height": "1.89"
}),
}),
})
.compile();
就我而言,我必须这样做
import { join } from 'path';
import { Env, NODE_ENV } from '@backend/utilities';
export const AUTH_SERVICE_PROTO_PATH =
NODE_ENV === Env.E2E
? 'apps/backend/users/src/proto/auth.proto'
: join(__dirname, 'proto/auth.proto');
因此,对于 e2e 测试,我们使用静态路径。