Scopes & Closures in Mocha/Chai Assertions - node.js

I'm writing some tests for an express app and I am wondering how to properly access a variable in one assertion block from another. The variable I am trying to access is this.token = res.body.token
Whenever I try to access it, it comes up undefined (other than when accessing it within the beforeEach block). How can I access this variable? I need to use the token to set the headers in my test for my POST request.
Code:
describe('CRUD: tests the GET & POST routes', () => {
beforeEach(done => {
chai.request('localhost:3000')
.post('/app/signup')
.send({ email: 'meow#test.com', password: 'testpass' })
.end((err, res) => {
if (err) return console.log(err);
this.token = res.body.token; // this variable holds a token when accessed within this scope (tested it with node debugger)
done();
});
});
it('should create with a new cat with a POST request', (done) => {
chai.request('localhost:3000')
.post('/app/cats')
.set('token', this.token) // when accessed here, it is undefined...
.send({ username: 'cat_user' })
.end((err, res) => {
expect(err).to.eql(null);
expect(res).to.have.status(200);
expect(res.body.name).to.eql('test cat');
expect(res.body).to.have.property('_id');
done();
});
});
EDIT: Here is a screenshot of my terminal in node debug mode. As you can see, when it hits the first debugger break and _token is accessed, it contains the token. In the next debugger break, however, it comes up empty... (maybe that means something else in the debugger?)

You can move your variable to the scope of your describe.
describe('CRUD: tests the GET & POST routes', () => {
let _token;
beforeEach(done => {
chai.request('localhost:3000')
.post('/app/signup')
.send({ email: 'meow#test.com', password: 'testpass' })
.end((err, res) => {
if (err) return console.log(err);
_token = res.body.token; // this variable holds a token when accessed within this scope (tested it with node debugger)
done();
});
});
it('should create with a new cat with a POST request', (done) => {
chai.request('localhost:3000')
.post('/app/cats')
.set('token', _token) // when accessed here, it is undefined...
.send({ username: 'cat_user' })
.end((err, res) => {
expect(err).to.eql(null);
expect(res).to.have.status(200);
expect(res.body.name).to.eql('test cat');
expect(res.body).to.have.property('_id');
done();
});
});
You should read this to understand this: http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/

Related

Chai-http is not checking second assertion when one inside one request

I am trying to get my token variable from /signing to provide it to the name change route. But the assertion is not always triggering. Can there be any better way to do this? Can I use async-await to solve this problem, if so, how?
describe("setName", function (done) {
it("/POST user setName", function (done) {
Users.remove({}, (err) => {
console.log(chalk.bgBlue(`Removing User`));
// done();
});
let user = {
"email": "tiwari.ai.harsh#gmail.com",
"password": "password",
"name": "Harsh Tiwari"
}
var requester = chai.request(app).keepOpen()
requester.post("/api/users/signin").send({
user
}).end((err_signin, res_signin) => {
let token = res_signin.body.user.token;
let name = "Name Changed"
requester.post("/api/users/setName").set({ authorization: `Token ${token}` }).send({
name
}).end((err, res) => {
res.should.have.status(200); <--------------------------- This is not working
});
done()
});
});
});
The current code will execute done before the requester.post("/api/users/setName") finish because it is an async execution.
To solve the issue, the done() should be specified after res.should.have.status(200);
describe('setName', function (done) {
// NOTE: I also moved remove function here to ensure user is removed correctly
before(function(done) {
Users.remove({}, (err) => {
console.log(chalk.bgBlue(`Removing User`));
done(); // NOTE: also specify done to tell mocha that async execution is finished
});
})
it('/POST user setName', function (done) {
let user = {
email: 'tiwari.ai.harsh#gmail.com',
password: 'password',
name: 'Harsh Tiwari',
};
var requester = chai.request(app).keepOpen();
requester
.post('/api/users/signin')
.send({
user,
})
.end((err_signin, res_signin) => {
let token = res_signin.body.user.token;
let name = 'Name Changed';
requester
.post('/api/users/setName')
.set({ authorization: `Token ${token}` })
.send({
name,
})
.end((err, res) => {
res.should.have.status(200);
done(); // NOTE: move here
});
});
});
});

Use JWT token in multiple test cases mocha node-js

How I create a reusable function which gives me a JWT token, so I can execute my test cases in which token is required without calling the login function again and again in each test case file
account.js
describe("Account", () => {
var token;
describe("/POST Login", () => {
it("it should gives the token", (done) => {
chai.request(server)
.post('api/v1/account')
.set('Accept', 'application/json')
.send({ "email": "john#gmail.com", "password": "123456" })
.end((err, res) => {
res.should.have.status(200);
res.body.should.have.property("token");
token = res.body.token //----------------TOKEN SET
done();
});
});
});
describe("/GET account", () => {
it("it should get the user account", (done) => {
chai.request(server)
.get('api/v1/account')
.set('x-auth-token', token)
.end((err, res) => {
res.should.have.status(200);
done();
});
});
});
});
category
describe("Category", () => {
var token;
//Login function duplicate in both the files
describe("/POST Login", () => {
it("it should gives the token", (done) => {
chai.request(server)
.post('api/v1/account')
.set('Accept', 'application/json')
.send({ "email": "john#gmail.com", "password": "123456" })
.end((err, res) => {
res.should.have.status(200);
res.body.should.have.property("token");
token = res.body.token //----------------TOKEN SET
done();
});
});
});
describe("/GET category", () => {
it("it should get the user account", (done) => {
chai.request(server)
.get('api/v1/account')
.set('x-auth-token', token)
.end((err, res) => {
res.should.have.status(200);
done();
});
});
});
});
I want to get the token from other file and used in different cases. What is the best approach todo this?
I'm basing my response on the fact that you have mentioned unit tests. Usually, with a unit test, you're testing a small piece of functionality. This means, that you want to test a small piece of logic within a bigger component/piece of logic and you are not interested in testing other components (like for example in your case testing your API. What you usually want to test is how your logic should behave in the case you will receive a 200 success response from the API or what happens with your logic if you receive a 400 or 500. I would recommend to mock your API calls for your testing by using a library like nock:
https://www.npmjs.com/package/nock
The way that you are trying to implement it it might be a bit more complicated. If you want to do this kind of testing I wouldn't pick jest/mocha as test runners. I would prepare a postman collection (probably you already have that) and then I would utilise newman to run my collection and actually do the integration testing that you want. You can read further here: Running collections on the command line with Newman
There are different approaches as well, but the one above can be a good one.
Use a before hook that would always login users and generates a token that can be used in your new test file.
let token;
before('Login user', async () => {
const response = await chai.request(server)
.post('api/v1/account')
.set('Accept', 'application/json')
.send({ "email": "john#gmail.com", "password": "123456" })
token = res.body.token;
});

What is the proper way to test routes secured with jwt token?

While the following test passes I feel I'm doing this wrong. Am I expected to log in every time i need to test a secure route? I've tried passing global vars around after i get the initial token but passing vars i'm finding extremely counter intuitive. Passing variables in a before() call presents me same issue as passing / accessing global vars inside nested promises.
describe('Users', function(done) {
var testToken = 'my-test-token'
it('logs in', function(done) { // <= Pass in done callback
var rT = 'tttttt'
chai.request(urlroot)
.post('/login')
.type('form')
.send({ email: 'test_user_1#this.com', password: '9999' })
.end(function(err, res) {
expect(res).to.have.status(200);
expect(res.body.token).to.be.a('string');
done()
});
});
it('gets all users', function(done) { // <= Pass in done callback
// console.log(urlroot + '/users');
chai.request(urlroot)
.post('/login')
.type('form')
.send({ email: 'test_user_1#this.com', password: '9999' })
.end(function(err, res) {
chai.request(urlapi)
.get('/users?secret_token='+res.body.token)
.end(function(err, res){
console.log('data', res.body);
// expect(res.body).to.be.json()
})
});
});
});
What I do is use before() method to call my authenticate service to get the token in the same way that the aplication would, and store into a variable.
Something like:
var token = "";
before(async () => {
//Get token
token = "Bearer " + await getToken();
});
Then, in every test you want to use the credentials use .set()
it('...', function (done) {
chai
.request(url)
.set("Authorization", token) //Call .set() before .get()
.get("/users")
//...
})

mocha, chai testing for post in nodejs

I am new to unit testing in node.js with sequelize.js. I followed this tutorial to implement the api. It is working fine and I need to test it for unit testing.
First I tried to test post operation for User class. Following code is to be tested using mocha.
// create a user
app.post('/api/users', (req, res) => {
User.create(req.body)
.then(user => res.json(user))
})
My attempt is as below.
var index = require('../index');
describe('POST testing on user', function () {
it('should add a row', function (done) {
index.post('/api/users').send({ 'name': 'he' })
.end(function (err, res) {
chai.expect(res.status).to.equal(200);
done();
})
});
});
Then it will give this error saying index.post is not a function. Where should I get wrong and how can I correct it to execute the test case.
Your logic is quite wrong. It should be:
describe('/POST Create User', () => {
it('Create User Testing', (done) => {
let user = {
'name': 'he'
}
chai.request('http://localhost:3000')
.post('/api/users')
.send(user)
.end((err, res) => {
});
});
});
});

Tests fail when 8th endpoint is created using generator angular-fullstack

The problem is as weird as the title. I have a project which I created using the generator angular-fullstack, which I am connecting to a MSSQL server using Sequelize (who the f uses MSSQL... client's demands) and everything has been working really well until I had to create the 8th endpoint (using angular-fullstack:endpoint).
Every time I created an endpoint all the test (automatically created and executed using mocha) would work except for the PATCH verb integration test, which I would just eliminate as I am not using PATCH at all.
After I created the 8th endpoint (doing the same I did for every other one) the integration tests created by the generator itself (the unit tests work perfectly) started to fail (not just the endpoint's test, but other tests that used to work before), and they fail randomly (sometimes 3 of them fail, sometimes 4, and sometimes they all work), which makes me think of some kind of race condition (which I wasn't able to find).
Findings:
The POST integration test "works" but the result doesn't show up in the database. The log shows a correct SQL command sent to the database:
INSERT INTO [Findings] ([name],[info],[createdAt],[updatedAt]) OUTPUT INSERTED.* VALUES (N'New Finding',N'This is the brand new finding!!!',N'2018-03-05 22:30:24.000',N'2018-03-05 22:30:24.000');, and it returns 201 as status.
When the status code returned is 500, the error is usually name: 'SequelizeDatabaseError',
message: 'Invalid object name \'Findings\'.', as if the Table didn't exist, but it does!
If you have any idea on what can I try, I will be more than grateful! (I have already searched everywhere I could think of, but it's even hard to search for this problem)
This is the file containing the last-endpoint-created's tests. I can add any other file that might help to find a possible solution!
'use strict';
/* globals describe, expect, it, beforeEach, afterEach */
var app = require('../..');
import request from 'supertest';
var newFinding;
describe('Finding API:', function() {
describe('GET /api/findings', function() {
var findings;
beforeEach(function(done) {
request(app)
.get('/api/findings')
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
findings = res.body;
done();
});
});
it('should respond with JSON array', function() {
expect(findings).to.be.instanceOf(Array);
});
});
describe('POST /api/findings', function() {
beforeEach(function(done) {
request(app)
.post('/api/findings')
.send({
name: 'New Finding',
info: 'This is the brand new finding!!!'
})
.expect(201)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
newFinding = res.body;
done();
});
});
it('should respond with the newly created finding', function() {
expect(newFinding.name).to.equal('New Finding');
expect(newFinding.info).to.equal('This is the brand new finding!!!');
});
});
describe('GET /api/findings/:id', function() {
var finding;
beforeEach(function(done) {
request(app)
.get(`/api/findings/${newFinding._id}`)
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
finding = res.body;
done();
});
});
afterEach(function() {
finding = {};
});
it('should respond with the requested finding', function() {
expect(finding.name).to.equal('New Finding');
expect(finding.info).to.equal('This is the brand new finding!!!');
});
});
describe('PUT /api/findings/:id', function() {
var updatedFinding;
beforeEach(function(done) {
request(app)
.put(`/api/findings/${newFinding._id}`)
.send({
name: 'Updated Finding',
info: 'This is the updated finding!!!'
})
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if(err) {
return done(err);
}
updatedFinding = res.body[0];
done();
});
});
afterEach(function() {
updatedFinding = {};
});
it('should respond with the updated finding', function() {
expect(updatedFinding).to.equal(1);
});
it('should respond with the updated finding on a subsequent GET', function(done) {
request(app)
.get(`/api/findings/${newFinding._id}`)
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
let finding = res.body;
expect(finding.name).to.equal('Updated Finding');
expect(finding.info).to.equal('This is the updated finding!!!');
done();
});
});
});
});
I found a workaround and I think I have found the problem, but I don't understand why it is happening now, and not before.
Some tests were being executed before the db connection was established, so I have added
before(function(done) {
app.on('appStarted', function() {
done();
});
});
to the new test file, and now it works without any problem!

Resources