I'm looking for a way to bring up a NestJS application with mocked providers. This is necessary for provider contract tests because a service needs to be brought up in isolation. Using the Pact library, testing the provider assumes that the provider service is already running. It needs to be able to make HTTP requests against the actual server (with some dependencies mocked if necessary). PactJS
I've looked into the docs for NestJS and the closest solution I can find is pasted below. From what I can tell, this solution tells the module to replace any provider called CatsService
with catsService
. This theoretically would work for provider contract testing purposes, but I don't think this allows for the entire app to be brought up, just a module. There is no mention in the docs for being able to bring up the app on a specific port using the testing module. I've tried to call app.listen
on the returned app object and it fails to hit a breakpoint placed right after the call.
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();
});
});
Using Spring a configuration class, mocks can be injected into the app when running with the "contract-test" profile.
@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);
}
}
Since version 4.4 you can also use listen
since it now also returns a Promise
.
You have to use the method listenAsync
instead of listen
so that you can use it with 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);
^^^^^^^^^^^^^^^^^^^^^
});
Then you can make actual http requests instead of relying on supertest. (I am using the nodejs standard http library in this example.)
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();
});
});
});
Don't forget to close the application or otherwise your test will run until closed manually.
afterAll(() => app.close());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With