在 NestJS 应用程序中注入模拟以进行合同测试
Injecting Mocks in NestJS Application for Contract Testing
问题
我正在寻找一种方法来启动带有模拟提供程序的 NestJS 应用程序。这对于提供者合同测试来说是必要的,因为需要隔离地启动服务。使用 Pact 库,测试提供者假定提供者服务已经 运行。它需要能够对实际服务器发出 HTTP 请求(必要时模拟一些依赖项)。 PactJS
当前研究
我查看了 NestJS 的文档,下面粘贴了我能找到的最接近的解决方案。据我所知,此解决方案告诉模块将任何名为 CatsService
的提供程序替换为 catsService
。这在理论上适用于提供商合同测试目的,但我认为这不允许启动整个应用程序,只是一个模块。文档中没有提到能够使用测试模块在特定端口上启动应用程序。我尝试在返回的应用程序对象上调用 app.listen
,但未能命中调用后立即放置的断点。
import * as request from "supertest";
import { Test } from "@nestjs/testing";
import { CatsModule } from "../../src/cats/cats.module";
import { CatsService } from "../../src/cats/cats.service";
import { INestApplication } from "@nestjs/common";
describe("Cats", () => {
let app: INestApplication;
let catsService = { findAll: () => ["test"] };
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [CatsModule]
})
.overrideProvider(CatsService)
.useValue(catsService)
.compile();
app = module.createNestApplication();
await app.init();
});
it(`/GET cats`, () => {
return request(app.getHttpServer())
.get("/cats")
.expect(200)
.expect({
data: catsService.findAll()
});
});
afterAll(async () => {
await app.close();
});
});
Java 例子
使用 Spring 配置 class,当 运行 "contract-test" 配置文件时,可以将模拟注入应用程序。
@Profile({"contract-test"})
@Configuration
public class ContractTestConfig {
@Bean
@Primary
public SomeRepository getSomeRepository() {
return mock(SomeRepository.class);
}
@Bean
@Primary
public SomeService getSomeService() {
return mock(SomeService.class);
}
}
更新
从 4.4 版开始,您还可以使用 listen
,因为它现在也 returns 和 Promise
。
您必须使用方法 listenAsync
而不是 listen
以便您可以将其与 await
:
一起使用
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
})
.overrideProvider(AppService).useValue({ root: () => 'Hello Test!' })
.compile();
app = moduleFixture.createNestApplication();
await app.init();
await app.listenAsync(3000);
^^^^^^^^^^^^^^^^^^^^^
});
然后你就可以发出实际的 http 请求,而不是依赖于 supertest。 (我在这个例子中使用的是 nodejs 标准 http 库。)
import * as http from 'http';
// ...
it('/GET /', done => {
http.get('http://localhost:3000/root', res => {
let data = '';
res.on('data', chunk => data = data + chunk);
res.on('end', () => {
expect(data).toEqual('Hello Test!');
expect(res.statusCode).toBe(200);
done();
});
});
});
不要忘记关闭应用程序,否则您的测试将 运行 直到手动关闭。
afterAll(() => app.close());
问题
我正在寻找一种方法来启动带有模拟提供程序的 NestJS 应用程序。这对于提供者合同测试来说是必要的,因为需要隔离地启动服务。使用 Pact 库,测试提供者假定提供者服务已经 运行。它需要能够对实际服务器发出 HTTP 请求(必要时模拟一些依赖项)。 PactJS
当前研究
我查看了 NestJS 的文档,下面粘贴了我能找到的最接近的解决方案。据我所知,此解决方案告诉模块将任何名为 CatsService
的提供程序替换为 catsService
。这在理论上适用于提供商合同测试目的,但我认为这不允许启动整个应用程序,只是一个模块。文档中没有提到能够使用测试模块在特定端口上启动应用程序。我尝试在返回的应用程序对象上调用 app.listen
,但未能命中调用后立即放置的断点。
import * as request from "supertest";
import { Test } from "@nestjs/testing";
import { CatsModule } from "../../src/cats/cats.module";
import { CatsService } from "../../src/cats/cats.service";
import { INestApplication } from "@nestjs/common";
describe("Cats", () => {
let app: INestApplication;
let catsService = { findAll: () => ["test"] };
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [CatsModule]
})
.overrideProvider(CatsService)
.useValue(catsService)
.compile();
app = module.createNestApplication();
await app.init();
});
it(`/GET cats`, () => {
return request(app.getHttpServer())
.get("/cats")
.expect(200)
.expect({
data: catsService.findAll()
});
});
afterAll(async () => {
await app.close();
});
});
Java 例子
使用 Spring 配置 class,当 运行 "contract-test" 配置文件时,可以将模拟注入应用程序。
@Profile({"contract-test"})
@Configuration
public class ContractTestConfig {
@Bean
@Primary
public SomeRepository getSomeRepository() {
return mock(SomeRepository.class);
}
@Bean
@Primary
public SomeService getSomeService() {
return mock(SomeService.class);
}
}
更新
从 4.4 版开始,您还可以使用 listen
,因为它现在也 returns 和 Promise
。
您必须使用方法 listenAsync
而不是 listen
以便您可以将其与 await
:
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
})
.overrideProvider(AppService).useValue({ root: () => 'Hello Test!' })
.compile();
app = moduleFixture.createNestApplication();
await app.init();
await app.listenAsync(3000);
^^^^^^^^^^^^^^^^^^^^^
});
然后你就可以发出实际的 http 请求,而不是依赖于 supertest。 (我在这个例子中使用的是 nodejs 标准 http 库。)
import * as http from 'http';
// ...
it('/GET /', done => {
http.get('http://localhost:3000/root', res => {
let data = '';
res.on('data', chunk => data = data + chunk);
res.on('end', () => {
expect(data).toEqual('Hello Test!');
expect(res.statusCode).toBe(200);
done();
});
});
});
不要忘记关闭应用程序,否则您的测试将 运行 直到手动关闭。
afterAll(() => app.close());