I was trying to write my first e2e test in nestjs using the following code:
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
AppModule,
]
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
await getConnection().synchronize(true);
});
it('/ (GET)', async () => {
return request(app.getHttpServer())
.get('/')
.expect(200);
});
afterAll(async () => {
await app.close();
});
});
It always fails with 404, when trying to debug the supertest request I saw:
{
errno: -61,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 57523,
response: undefined
}
If I try to connect to the actual app using .agent('127.0.0.1:3000') the test passes.
it('/ (GET)', async () => {
return request
.agent('127.0.0.1:3000')
.get('/')
.expect(200)
});
You have to await the response. Do something like. Look at below snippet where i have added await to response instead of return
it('should throw validation error when creating employer policy', async () => {
const data = {
max_percent_of_salary: true,
is_employer_paid: true,
has_advance_activated: false,
};
const response = await request(app.getHttpServer())
.post('/sa-employer-policy/create/')
.set('Accept', 'application/json')
.set('Authorization', 'Bearer ' + employer_token)
.send(data)
.expect(HttpStatus.BAD_REQUEST);
expect(response.body.message).toBeDefined();
});
The problem was that I was using an endpoint from a controller without importing the parent module.
Related
I’m writing a unit test that requires me to send a mutation via the apollo client to my http server (created using nestjs). I’m running it locally.
Here is the entire unit test:
import { INestApplication } from '#nestjs/common';
import { Test, TestingModule } from '#nestjs/testing';
import { AppModule } from '#/core/app.module';
import { ApolloClient, InMemoryCache, gql, HttpLink } from '#apollo/client/core';
import fetch from 'cross-fetch';
describe('User (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule]
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
describe('MUT createUser()', () => {
const client = new ApolloClient({
link: new HttpLink({ uri: 'localhost:3000/graphql', fetch }),
cache: new InMemoryCache()
});
const mutation = () => {
try {
const mutate = gql`
mutation createUser($userCreateInput: CreateUserDto!) {
userCreateInput(
userCreateInput: {
username: "bob"
email: "bob#bob.com"
password: "bobby"
}
)
active
createdAt
email
emailVerified
id
updatedAt
username
}`;
console.log(mutate, 'in thing');
return mutate;
} catch (e) {
console.log(e);
}
};
it('should return a new user and then delete that user right after', async () => {
try {
const createdUser = await client.mutate({ mutation: mutation() });
console.log(createdUser);
} catch (e) {
console.log(e);
}
});
});
});
After bunch of syntax errors I’ve finally gotten this error which is printed in my catch statement at the bottom:
ApolloError: Only HTTP(S) protocols are supported
at new ApolloError (/home/paul/nest-nuxt-starter/node_modules/.pnpm/#apollo+client#3.6.9_graphql#15.7.2/node_modules/#apollo/client/errors/index.js:29:28)
at Object.error (/home/paul/nest-nuxt-starter/node_modules/.pnpm/#apollo+client#3.6.9_graphql#15.7.2/node_modules/#apollo/client/core/QueryManager.js:139:83)
at notifySubscription (/home/paul/nest-nuxt-starter/node_modules/.pnpm/zen-observable#0.8.15/node_modules/zen-observable/lib/Observable.js:140:18)
at onNotify (/home/paul/nest-nuxt-starter/node_modules/.pnpm/zen-observable#0.8.15/node_modules/zen-observable/lib/Observable.js:179:3)
at SubscriptionObserver.error (/home/paul/nest-nuxt-starter/node_modules/.pnpm/zen-observable#0.8.15/node_modules/zen-observable/lib/Observable.js:240:7)
at Object.error (/home/paul/nest-nuxt-starter/node_modules/.pnpm/#apollo+client#3.6.9_graphql#15.7.2/node_modules/#apollo/client/utilities/observables/asyncMap.js:32:69)
at notifySubscription (/home/paul/nest-nuxt-starter/node_modules/.pnpm/zen-observable#0.8.15/node_modules/zen-observable/lib/Observable.js:140:18)
at onNotify (/home/paul/nest-nuxt-starter/node_modules/.pnpm/zen-observable#0.8.15/node_modules/zen-observable/lib/Observable.js:179:3)
at SubscriptionObserver.error (/home/paul/nest-nuxt-starter/node_modules/.pnpm/zen-observable#0.8.15/node_modules/zen-observable/lib/Observable.js:240:7)
at /home/paul/nest-nuxt-starter/node_modules/.pnpm/#apollo+client#3.6.9_graphql#15.7.2/node_modules/#apollo/client/utilities/observables/iteration.js:4:68 {
graphQLErrors: [],
clientErrors: [],
networkError: TypeError: Only HTTP(S) protocols are supported
at getNodeRequestOptions (/home/paul/nest-nuxt-starter/node_modules/.pnpm/node-fetch#2.6.7/node_modules/node-fetch/lib/index.js:1331:9)
at /home/paul/nest-nuxt-starter/node_modules/.pnpm/node-fetch#2.6.7/node_modules/node-fetch/lib/index.js:1440:19
at new Promise (<anonymous>)
at fetch (/home/paul/nest-nuxt-starter/node_modules/.pnpm/node-fetch#2.6.7/node_modules/node-fetch/lib/index.js:1437:9)
at fetch (/home/paul/nest-nuxt-starter/node_modules/.pnpm/cross-fetch#3.1.5/node_modules/cross-fetch/dist/node-ponyfill.js:10:20)
at /home/paul/nest-nuxt-starter/node_modules/.pnpm/#apollo+client#3.6.9_graphql#15.7.2/node_modules/#apollo/client/link/http/createHttpLink.js:96:13
at new Subscription (/home/paul/nest-nuxt-starter/node_modules/.pnpm/zen-observable#0.8.15/node_modules/zen-observable/lib/Observable.js:197:34)
at Observable.subscribe (/home/paul/nest-nuxt-starter/node_modules/.pnpm/zen-observable#0.8.15/node_modules/zen-observable/lib/Observable.js:279:14)
at Object.complete (/home/paul/nest-nuxt-starter/node_modules/.pnpm/#apollo+client#3.6.9_graphql#15.7.2/node_modules/#apollo/client/utilities/observables/Concast.js:60:43)
at Concast.Object.<anonymous>.Concast.start (/home/paul/nest-nuxt-starter/node_modules/.pnpm/#apollo+client#3.6.9_graphql#15.7.2/node_modules/#apollo/client/utilities/observables/Concast.js:86:23),
extraInfo: undefined
}
I assumed that client.mutate would be http(s). why isn’t it?
I'm using JestJS for end-to-end testing of my NestJS API
When I run all end-to-end tests (which are split up across many files, each file contains one test suite):
'npm run test:e2e'
Multiple tests are filing (different ones everytime).
When I run each test suite individually, the tests pass 100% of the time.
Here's one of my test files:
describe('Cameras - /components/cameras (e2e)', () => {
const camerasRequest = {
CameraSerialNumber: 1,
Status: "online",
};
const camerasResponse = {
CameraSerialNumber: 1,
Status: "online",
};
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
username: 'root',
password: 'pass',
database: 'test',
autoLoadEntities: true,
synchronize: false,
}),
CamerasModule,
],
providers: [{
provide: APP_FILTER,
useClass: GlobalExceptionFilter,
}],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
describe("Response code: 200", () => {
it('Delete one camera [DELETE /components/cameras/:id]', async () => {
await request(app.getHttpServer())
.post('/components/cameras')
.send(camerasRequest as CreateCameraDto)
await request(app.getHttpServer())
.delete('/components/cameras/1')
.expect(200)
.then(({ body }) => {
expect(body.affected).toStrictEqual(1);
});
await request(app.getHttpServer())
.get('/components/cameras/1')
.expect(200)
.then(({ body }) => {
expect(body).toStrictEqual({});
});
return
});
it('Delete one camera with unknown camera serial number [DELETE /components/cameras/:id]', async () => {
await request(app.getHttpServer())
.delete('/components/cameras/99')
.expect(200)
.then(({ body }) => {
expect(body.affected).toStrictEqual(0)
});
return
});
});
describe("Response code: 400", () => {
});
afterEach( async () => {
await request(app.getHttpServer())
.delete('/components/cameras/1');
await request(app.getHttpServer())
.delete('/components/cameras/2');
return;
})
});
My guess is that the problem lies within the way Jest parallelizes the execution of my test suites... I can't find a fix for this. What can I do?
This is channel.service.spec.ts. i am beginner in nodejs and testing service class. how can i solve this error. thanks
describe('ChannelService', () => {
let service: ChannelService;
let policy: Policy[]
const mockChannelService = {
getChannelPolicies: jest.fn().mockReturnValue([policy]),
};
beforeEach(async () => {
GeonoonNacos(rootPath, settings, false, true);
const module: TestingModule = await Test.createTestingModule({
providers: [ChannelService],
})
.overrideProvider(ChannelService)
.useValue(mockChannelService)
.compile();
service = module.get<ChannelService>(ChannelService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
// Unit test for channel-Service to get related policies of channel from db
it('should get related policies of channel from db', async () => {
expect(await service.getChannelPolicies(5)).toHaveBeenCalled();
});
});
can anyone help me out creating a fake response (500) for testing my API using "sinon" , am new to nodeJs , i have tried to test where the return status is 201 and it worked however am still not able to make the fake 500 response
here is my code , thank you in advance
//knex
app.post("/api/categories", function (req, rep) {
knex('categories').insert(req.body)
.then(() => rep.sendStatus(201).json({ message: "Category inserted" }))
.catch((err) => {
console.log(err)
rep.status(500);
})
});
// in my test js
var request=require('supertest');
var KnexApp=require('../KnexFolder/app');
var sinon = require("sinon");
describe("POST/users", ()=>{
describe('when everything is fine and no errors', () => {
it('should respond with status 201',async () => {
const res = await request(KnexApp)
.post('/api/categories')
.send({
name:"from test",
img_id: 5
})
expect(res.statusCode).toEqual(201)
})
})
describe('when There is internal server error', () => {
it('should respond with status 500',async () => {
sinon.stub('/api/categories', "post").throws(
new Error({
response: { status: 500},
})
);
expect(res.statusCode).toEqual(500)
})
})
})
There are two testing strategies:
Stub knex, query interface, and the resolved/rejected value. This way is easier than the second way, you don't need to set up a real testing database and populate testing data.
As mentioned above, you need to set up a real testing database(run your migration script to create database and tables, create the seed testing data, etc...)
I will use the first way to test your code. Since sinon doesn't support stub a function export defaults by a module. We need to use proxyquire package.
app.js:
const express = require('express');
const knex = require('knex')({
client: 'mysql',
connection: {
host: '127.0.0.1',
port: 3306,
user: 'your_database_user',
password: 'your_database_password',
database: 'myapp_test',
},
});
const app = express();
app.use(express.json());
app.post('/api/categories', function (req, rep) {
console.log(req.body);
knex('categories')
.insert(req.body)
.then(() => rep.sendStatus(201).json({ message: 'Category inserted' }))
.catch((err) => {
console.log(err);
rep.sendStatus(500);
});
});
module.exports = app;
app.test.js:
const request = require('supertest');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
describe('POST/users', () => {
describe('when everything is fine and no errors', () => {
it('should respond with status 201', async () => {
const queryInterfaceStub = {
insert: sinon.stub().resolves(),
};
const knexStub = sinon.stub().returns(queryInterfaceStub);
const KnexStub = sinon.stub().returns(knexStub);
const KnexApp = proxyquire('./app', {
knex: KnexStub,
});
const res = await request(KnexApp).post('/api/categories').send({
name: 'from test',
img_id: 5,
});
sinon.assert.match(res.statusCode, 201);
});
});
describe('when There is internal server error', () => {
it('should respond with status 500', async () => {
const queryInterfaceStub = {
insert: sinon.stub().rejects(new Error('fake error')),
};
const knexStub = sinon.stub().returns(queryInterfaceStub);
const KnexStub = sinon.stub().returns(knexStub);
const KnexApp = proxyquire('./app', {
knex: KnexStub,
});
const res = await request(KnexApp).post('/api/categories').send({});
sinon.assert.match(res.statusCode, 500);
});
});
});
Test result:
POST/users
when everything is fine and no errors
{ name: 'from test', img_id: 5 }
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:558:11)
at ServerResponse.header (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/express/lib/response.js:771:10)
at ServerResponse.send (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/express/lib/response.js:267:15)
at /Users/dulin/workspace/github.com/mrdulin/expressjs-research/src/stackoverflow/71565119/app.js:20:37
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
code: 'ERR_HTTP_HEADERS_SENT'
}
✓ should respond with status 201 (436ms)
when There is internal server error
{}
Error: fake error
at Context.<anonymous> (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/src/stackoverflow/71565119/app.test.js:27:38)
at callFn (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runnable.js:364:21)
at Test.Runnable.run (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runnable.js:352:5)
at Runner.runTest (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:677:10)
at /Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:801:12
at next (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:594:14)
at /Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:604:7
at next (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:486:14)
at Immediate.<anonymous> (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:572:5)
at processImmediate (internal/timers.js:461:21)
✓ should respond with status 500
2 passing (448ms)
Imagine I have a Controller defined like so:
class NewsEndpointQueryParameters {
#IsNotEmpty()
q: string;
#IsNotEmpty()
pageNumber: number;
}
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get(['', 'ping'])
ping(): PingEndpointResponse {
return this.appService.ping();
}
#Get(['news'])
getNews(
#Query() queryParameters: NewsEndpointQueryParameters
): Observable<NewsEndpointResponse> {
return this.appService.getNews(
queryParameters.q,
queryParameters.pageNumber
);
}
}
I want to be able to test what happens in a request, if, for example, a query parameter is not provided.
Right now this is my testing setup:
describe('AppController', () => {
let app: TestingModule;
let nestApp: INestApplication;
let appService: AppService;
beforeAll(async () => {
app = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
imports: [HttpModule],
}).compile();
appService = app.get<AppService>(AppService);
nestApp = app.createNestApplication();
await nestApp.init();
return;
});
describe('/', () => {
test('Return "Pong!"', async () => {
const appServiceSpy = jest.spyOn(appService, 'ping');
appServiceSpy.mockReturnValue({ message: 'Pong!' });
const response = await supertest(nestApp.getHttpServer()).get('/');
expect(response.body).toStrictEqual({
message: 'Pong!',
});
return;
});
});
describe('/ping', () => {
test('Return "Pong!"', async () => {
const appServiceSpy = jest.spyOn(appService, 'ping');
appServiceSpy.mockReturnValue({ message: 'Pong!' });
const response = await supertest(nestApp.getHttpServer()).get('/ping');
expect(response.body).toStrictEqual({
message: 'Pong!',
});
return;
});
});
describe('/news', () => {
describe('Correct query', () => {
beforeEach(() => {
const appServiceSpy = jest.spyOn(appService, 'getNews');
appServiceSpy.mockReturnValue(
new Observable<NewsEndpointResponse>((subscriber) => {
subscriber.next({
data: [{ url: 'test' }],
message: 'test',
status: 200,
});
subscriber.complete();
})
);
return;
});
test('Returns with a custom body response.', async () => {
const response = await supertest(nestApp.getHttpServer()).get(
'/news?q=test&pageNumber=1'
);
expect(response.body).toStrictEqual({
data: [{ url: 'test' }],
message: 'test',
status: 200,
});
return;
});
return;
});
describe('Incorrect query', () => {
test("Returns an error if 'q' query parameter is missing.", async () => {
return;
});
test("Returns an error if 'pageNumber' query parameter is missing.", async () => {
return;
});
return;
});
return;
});
return;
});
If I do nx serve and then curl 'localhost:3333/api/ping', I get:
{"message":"Pong!"}
And if I do curl 'localhost:3333/api/news?q=test&pageNumber=1' I get:
{"data":['lots of interesting news'],"message":"News fetched successfully!","status":200}
Finally, if I do curl 'localhost:3333/api/news?q=test' I get:
{"statusCode":400,"message":["pageNumber should not be empty"],"error":"Bad Request"}
How can I replicate the last case? If I use supertest, there is no error returned like the above. I haven't found a way to mock the Controller's function too.
A very special thank to #jmcdo29 for explaining me how to do this.
Code:
beforeAll(async () => {
app = await Test.createTestingModule({
controllers: [AppController],
providers: [
AppService,
{ provide: APP_PIPE, useValue: new ValidationPipe() },
],
imports: [HttpModule, AppModule],
}).compile();
appService = app.get<AppService>(AppService);
nestApp = app.createNestApplication();
await nestApp.init();
return;
});
Explanation:
We need to model the behavior of bootstrap() in main.ts. In my case, in looks like this:
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
cors: environment.nestCors,
});
app.useGlobalPipes(new ValidationPipe());
const globalPrefix = 'api';
app.setGlobalPrefix(globalPrefix);
const port = process.env.PORT || 3333;
await app.listen(port, () => {
Logger.log('Listening at http://localhost:' + port + '/' + globalPrefix);
});
}
Instead of importing the AppModule, we could also configure the app created for testing like so: nestApp.useGlobalPipes(new ValidationPipe()) (this needs to be done before await nestApp.init())