I'm going off of this tutorial, trying to make tests with Mocha, Supertest, and Should.js.
I have the following basic test to create a user through a PUT endpoint that accepts it's data in headers.
describe('User Routes', function () {
it('should allow me to make my user', function (done) {
request(url)
.put('/users')
.set(myCreds)
// end handles the response
.end(function(err, res) {
if (err) {
throw err;
}
// this is should.js syntax, very clear
res.should.have.status(201);
done();
});
});
However, while the endpoint does trigger, and the user does get made, the code throws an error that should is undefined... Uncaught TypeError: undefined is not a function.
I have
var should = require('should');
var assert = require('assert');
var request = require('supertest');
At the top of the file, so why would it be undefined?
You are calling should incorrectly, try this:
res.should.have.property('status', 201)
or
res.status.should.be.equal(201)
or
should.equal(res.status, 201)
or install should-http.
Related
I am attempting to grab data from an API from openWeatherAPI with a correct api key and query (I checked with Postman to ensure the call is correct), but ran into a syntax error. When I try to call the on() function inside of my https.get callback function, I am met with the following error in my terminal:
response.on("data", (data) => {
^
TypeError: Cannot read properties of undefined (reading 'on')
at ClientRequest.<anonymous> (C:file-path\api-prac\app.js:16:18)
at Object.onceWrapper (node:events:628:26)
at ClientRequest.emit (node:events:513:28)
at HTTPParser.parserOnIncomingClient [as onIncoming] (node:_http_client:693:27)
at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
at TLSSocket.socketOnData (node:_http_client:534:22)
at TLSSocket.emit (node:events:513:28)
at addChunk (node:internal/streams/readable:315:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at TLSSocket.Readable.push (node:internal/streams/readable:228:10)
My code:
const express = require("express");
const https = require("https");
const app = express()
// what should happen when user tries to go to home page
app.get("/", function(req, res) {
const url = "https://api.openweathermap.org/data/2.5/weather?q=London&appid=my-api-key";
https.get(url, function(req, response) {
console.log("blah blah repsonse");
response.on("data", (data) => {
console.log(data);
// const weatherDatta = JSON.parse(data)
/* extra code will be put here to send a response */
})
});
res.send("server is up");
}
app.listen(3000, function() {
console.log("app running on server 3000");
})
I tried looking at the documentation shown on the https://nodejs.org/api/https.html website, but was unable to find anything that helped outside of what I was already doing with my code.
The arguments for your https.get() callback are wrong. It should be this:
https.get(url, function(response) {
response.on('data', ...);
});
There is no second argument so when you try to make one, it's undefined and does not work.
Code example in the doc here.
Note also that there is no guarantee that you get the entire response in the first data event. The response may arrive in chunks so if you're trying to get the whole response, you should be accumulating all the data events and then processing them all in the end event. And, you should be handling errors in multiple places:
https.get(url, function(response) {
let result = "";
response.on('data', data => {
result += data.toString();
}).on('end', () => {
try {
let weatherData = JSON.parse(result);
// use the weatherData here
} catch(e) {
console.log(e);
// handle JSON parsing error here
}
}).on('error', err => {
console.log(err);
// handle http request error here
});
});
Note, using an http request library such as got() or node-fetch() or even fetch() which is built-in to the newest versions of nodejs will make this code much simpler because they will retrieve the entire response for you and are promise based which makes a number of things including error handling much simpler.
Note how much simpler this is with the got() library.
got(url).json().then(weatherData => {
// use weatherData here
}).catch(err => {
console.log(err);
// handler error here
});
I use npm install ldapjs to use this library. And simple bind code:
var ldap = require('ldapjs');
var client = ldap.createClient({
url: 'ldap://192.168.1.90:389'
})
const baseDN = 'dc=mnw-inc,dc=com'
export function login(username, password) {
client.bind(rootDN, rootpasswd, function(err, res) {
// here handle response
})
}
This is a react app and I run the app. I got this error in consol
Uncaught TypeError: net.connect is not a function
at connectSocket (client.js:1039)
at Backoff.<anonymous> (client.js:1203)
at Backoff../node_modules/events/events.js.EventEmitter.emit (events.js:84)
at Backoff../node_modules/backoff/lib/backoff.js.Backoff.onBackoff_ (backoff.js:53)
When I call the login() function nothing return at all.
What could I do?
I am simply trying to work within my app.js or router file to handle possible missing router errors. (similar stackoverflow solutions have not worked for me)
my app.js or router files is thus (works fine as is; but am trying to handle missing router error properly):
'use strict'
var path = require('path');
var film = require('./healthymeals.js');
var express = require('express');
var router = express.Router();
var app = express();
module.exports = function(app) {
app.get('/allMeals', film.getAllMeals);
app.get('/meals/:id', film.getMealInfo);
app.get('/meals/:id/options',meal.getHealthyMealoptions);
app.get('*', function(req, res) {
res.send("Healthy meals");
});
I have installed npm's/express error handling package 'http-status-codes' .
And tried it's implementation:
var HttpStatus = require('http-status-codes');
response
.status(HttpStatus.OK)
.send('ok');
response
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.send({
error: HttpStatus.getStatusText(HttpStatus.INTERNAL_SERVER_ERROR)
});
when the above is ran; I get a build error in terminal thrown on the 'response' word. I had no errors when installing relevant npm package.
ReferenceError: response is not defined
I have then tried a few suggestions I found on stackover, ie.
function getAllApps(request, response) {
appService.getApps(request.query.$expand).then(function (apps) {
response.status(200).send(apps);
})
.catch(function (err) {
console.error('Error occurred in Apps Api: ' + err);
response.status(500).send("" + err);
});
}
which is not effective and seems to just get ignored. any pointers appreciated (first time using this stack). cheers
Update, with the answer below from Jonas w - I was first getting time out error as follows:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
And then after increasing time out, I am getting below error:
TypeError: Cannot read property 'status' of undefined
For more context here is the test in which it is not passing with my attempts:
describe('error handling', function() {
it('handles missing routes', function(done) {
request
.get('/meals/brokenroute')
.expect(404)
.expect(function(res) {
ok('message' in res.body, '"message" key missing');
})
.end(done);
});
The best solution ive found yet ( using plain express only):
app.get('/allMeals', film.getAllMeals, END);
app.get('/meals/:id', film.getMealInfo, END);
app.get('/meals/:id/options',meal.getHealthyMealoptions,END);
function END(req,res){
res.end();
//note that this wont call next...
}
//all errors are handled below
app.get('*', function(req, res) {
res.status(404).end();
});
var database = require('database');
var express = require('express');
var app = express();
var cors = require('cors');
app.use(cors());
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({
extended: false
});
app.post('/dosomething', urlencodedParser, function(req, res) {
if (!req.body.a) {
res.status(500).send(JSON.stringify({
error: 'a not defined'
}));
return;
}
firstAsyncFunction(req.body.a, function(err, result) {
if (err) {
res.status(500).send('firstAsyncFunction was NOT a success!');
} else {
if (result.b) {
secondAsyncFunction(result.b, function(err, data) {
if (err) {
res.status(500).send('secondAsyncFunction was NOT a success!');
return;
}
res.send('EVERYTHING WAS A SUCCESS! ' + data);
});
}
else {
res.status(500).send('result.b is not defined');
}
}
});
});
function firstAsyncFunction(param, callback) {
//Some network call:
// Return either return (callback(null,'success')); or return (callback('error'));
var query = database.createQuery(someOptionsHere);
database.runDatabaseQuery(query, function(err, entities, info) {
if (err) {
return (callback('error'));
}
return (callback(null, 'success'));
});
};
function secondAsyncFunction(param, callback) {
//Some network call:
// Return either return (callback(null,'success')); or return (callback('error'));
var query = database.createQuery(someOptionsHere);
database.runDatabaseQuery(query, function(err, entities, info) {
if (err) {
return (callback('error'));
}
return (callback(null, 'success'));
});
};
var server = app.listen(process.env.PORT || 3000, function() {
var host = server.address().address;
var port = server.address().port;
console.log('App listening at http://%s:%s', host, port);
});
module.exports = app;
I have here a basic express http server. This server has one route, dosomething, which makes two network calls and tells the user if they were a success or not.
This is my entire webserver (this is a bare bones server of my actual server for example purposes). I am now concerned with this server crashing. Reading the docs for express I see there is a default error handler which will catch errors and prevent the server from crashing (http://expressjs.com/en/guide/error-handling.html). I have added the code:
function defaultErrorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
res.status(500);
res.render('error', { error: err });
}
app.use(defaultErrorHandler);
This still crashes my server though. For example. I had a problem with my database returning an improper JSON response and inside of my firstAsyncFunction (not shown in the code) I tried to parse the JSON and it caused an error telling me it was improper JSON and the server crashed and was unable to take requests anymore until I restarted it. I would like to avoid this and have the default error handler send out a generic response back to the user when this occurs. I thought if I specified the defaultErrorHandler and put it inside of app.use that it would capture and handle all errors, but this does not seem to be the case? Inside of my async function for example you can see I am looking if an error was returned and if it was I send an error back to the user, but what if some other error occurs, how can I get express to capture and handle this error for me?
The defaultErrorHandler cannot handle exceptions that are thrown inside asynchronous tasks, such as callbacks.
If you define a route like:
app.get('/a', function(req, res) {
throw new Error('Test');
});
An error will be thrown, and in this case defaultErrorHandler will successfully catch it.
If the same exception occurs in an async manner, like so:
app.get('/a', function(req, res) {
setTimeout(function () {
throw new Error('Test');
}, 1000);
});
The server will crush, because the callback is actually in another context, and exceptions thrown by it will now be caught by the original catcher. This is a very difficult issue to deal with when it comes to callback.
There is more than one solution though. A possible solution will be to wrap every function that is prone to throw error with a try catch statement. This is a bit excessive though.
For example:
app.get('/a', function(req, res) {
setTimeout(function () {
try {
var x = JSON.parse('{');
}
catch (err) {
res.send(err.message);
}
}, 1000);
});
A nicer solution:
A nicer solution, would be to use promises instead, if it's possible, then for example you can declare a single errorHandler function like so:
function errorHandler(error, res) {
res.send(error.message);
}
Then, let's say you have to following function with fetches stuff from the database (I used setTimeout to simulate async behavior):
function getStuffFromDb() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("{");
}, 100);
});
}
Notice that this function returns an invalid JSON string. Your route will look something like:
app.get('/a', function(req, res) {
getStuffFromDb()
.then(handleStuffFromDb)
.catch(function (error) { errorHandler(error, res) });
});
function handleStuffFromDb(str) {
return JSON.parse(str);
}
This is a very simplified example, but you can add a lot more functionality to it, and (at least theoretically) have a single catch statement which will prevent your server from crushing.
I have some Sails.js API tests (using Mocha) that make use of SuperTest's .end() method to run some Chai assertions on the response.
I call the test's done() callback after the assertions, but if an assertion error is thrown, the test times out.
I can wrap the assertions in a try/finally, but this seems a bit icky:
var expect = require('chai').expect;
var request = require('supertest');
// ...
describe('should list all tasks that the user is authorized to view', function () {
it('the admin user should be able to see all tasks', function (done) {
var agent = request.agent(sails.hooks.http.app);
agent
.post('/login')
.send(userFixtures.admin())
.end(function (err, res) {
agent
.get('/api/tasks')
.expect(200)
.end(function (err, res) {
try {
var tasks = res.body;
expect(err).to.not.exist;
expect(tasks).to.be.an('array');
expect(tasks).to.have.length.of(2);
} finally {
done(err);
}
});
});
});
});
Any suggestions on how to better deal with this? Perhaps Chai HTTP might be better?
According to Supertest's documentation, you need to check for the presence of err and, if it exists, pass it to the done function. Like so
.end(function (err, res) {
if (err) return done(err);
// Any other response-related assertions here
...
// Finish the test
done();
});
You can pass out login logic from test.
// auth.js
var request = require('supertest'),
agent = request.agent;
exports.login = function(done) {
var loggedInAgent = agent(sails.hooks.http.app);
loggedInAgent
.post('/login')
.send(userFixtures.admin())
.end(function (err, res) {
loggedInAgent.saveCookies(res);
done(loggedInAgent);
});
};
And then just use it in your test:
describe('should list all tasks that the user is authorized to view', function () {
var admin;
before(function(done) {
// do not forget to require this auth module
auth.login(function(loggedInAgent) {
admin = loggedInAgent;
done();
});
});
it('the admin user should be able to see all tasks', function (done) {
admin
.get('/api/tasks')
.expect(function(res)
var tasks = res.body;
expect(tasks).to.be.an('array');
expect(tasks).to.have.length.of(2);
})
.end(done);
});
it('should have HTTP status 200', function() {
admin
.get('/api/tasks')
.expect(200, done);
});
});
With such approach you should not login your admin for each test (you can reuse your admin again and again in your describe block), and your test cases become more readable.
You should not get timeouts with this approach because .end(done) guaranties that your test will finish without errors as well with them.