javascript - Inyección de Mocks en Aplicación NestJS para Pruebas de Contrato
Problema
Estoy buscando una manera de levantar una aplicación NestJS con proveedores simulados. Esto es necesario para las pruebas de contrato de proveedor porque un servicio necesita ser levantado de forma aislada. Usando la biblioteca Pact, probar el proveedor asume que el servicio del proveedor ya está en funcionamiento. Necesita ser capaz de hacer solicitudes HTTP contra el servidor real (con algunas dependencias simuladas si es necesario). PactJS
Investigación Actual
He revisado la documentación de NestJS y la solución más cercana que pude encontrar está pegada a continuación. Por lo que puedo entender, esta solución le indica al módulo que reemplace cualquier proveedor llamado CatsService por catsService. Teóricamente, esto funcionaría para fines de pruebas de contrato del proveedor, pero no creo que esto permita levantar toda la aplicación, solo un módulo. No hay mención en la documentación sobre la posibilidad de levantar la aplicación en un puerto específico usando el módulo de pruebas. He intentado llamar a app.listen sobre el objeto app devuelto y falla al intentar alcanzar un punto de interrupción colocado justo después de la llamada.
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();
});
});
Ejemplo de Java
Usando una clase de configuración de Spring, los mocks pueden ser inyectados en la aplicación al ejecutarse con el perfil "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);
}
}
Respuesta #1
Actualizar
Desde la versión 4.4 también puedes usar listen ya que ahora también devuelve un Promise.
Tienes que usar el método listenAsync en lugar de listen para que puedas usarlo con 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);
^^^^^^^^^^^^^^^^^^^^^
});
Entonces puedes hacer solicitudes HTTP reales en lugar de depender de supertest. (Estoy usando la biblioteca http estándar de Node.js en este ejemplo.)
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();
});
});
});
No olvides cerrar la aplicación o de lo contrario tu prueba continuará ejecutándose hasta que se cierre manualmente.
afterAll(() => app.close());
