網站搜尋

javascript - 在 NestJS 應用中注入模擬以進行契約測試


問題

我正在尋找一種方法來啟動具有模擬提供者的 NestJS 應用程式。這對於提供者契約測試是必要的,因為某個服務需要被單獨啟動。使用 Pact 函式庫時,測試提供者假設提供者服務已經在運行中。它需要能夠對實際伺服器發出 HTTP 請求(如果有必要,可模擬部分依賴)。PactJS

目前的研究

我已經查看了 NestJS 的文件,我能找到的最接近的解決方案如下所示。據我所知,這個解決方案告訴模組將任何名為 CatsService 的提供者替換為 catsService。理論上,這可以用於提供者契約測試,但我認為這並不允許整個應用程式啟動,只能啟動一個模組。文件中沒有提到可以使用測試模組在特定端口啟動應用。我嘗試在返回的 app 對象上調用 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 的配置類別,在使用 "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);
  }
} 

答案#1

更新

自從版本 4.4,你也可以使用 listen,因為它現在也會返回一個 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());