nodejs nock http get always returns 503 - node.js

I want to use nock for testing some http calls but it always retunrs 503 service unavailable
describe('BASIC API AUTH TESTS', function() {
'use strict';
before(async () => {
if (!nock.isActive()) nock.activate();
});
after(async() => {
nock.cleanAll();
await helpers.removeTestUsers(mongoDb);
});
it('should return nock response', async() => {
if (!nock.isActive()) nock.activate();
nock('http://zombo.com').get('/').times(1).reply(200, 'Ok');
http.get('http://zombo.com/'); // expected 200 got 503 service unavailable
console.log('nock.pendingMocks(): ', nock.pendingMocks()); // [ 'GET http://zombo.com:80/' ]
});
what is wrong?
testrunner: mocha
node version: 8.12
nock: 13.0.4

This issue is most likely related to the fact that you're not waiting for the request to respond (via Nock) before exiting the tests. Therefore nock.cleanAll(); in your after block is removing your nock and the http.get is attempting a live call.
If you want to stick with native Node http, you need to ditch async (or get fancy with Promises). Node docs have an example on how to use callbacks to wait for the response, then you can call the Mocha done function.
If you want to stick with async, I recommend an HTTP client lib that support promises, then await the request.

Related

How to handle "UnhandledPromiseRejectionWarning" in Fastify without "async / await" or ".catch"

I'm running a simple Fastify server and when I make a request and it fails, I wanna handle that exception in the setErrorHandler.
How can I achieve that?
This doesn't seem to work:
import fastify from 'fastify';
import fetch from 'node-fetch';
const app = fastify();
app.setErrorHandler(async (error, request, response) => {
// I want this to be called whenever there is an error
console.log('setErrorHandler');
return 'error';
});
app.get('/', (request, response) => {
// I do not want to use async / await or .catch here
fetch('https://...').then(() => { response.send(); });
});
I get the error UnhandledPromiseRejectionWarning and the server never sends a response.
I do NOT want to use async, await or .catch on the promise. The reason is I'm simulating a developer error. So if someone forgets to add a .catch and is not using async / await, I still wanna "catch" that error and return status 500 to the client.
I'm willing to change / wrap the request library adding a .catch in it, I just don't wanna change that endpoint handler code in general, since it's sort of out of my control (another developer might code it any way they want).
The reason is I'm simulating a developer error.
You can't manage these errors at runtime to reply to a request because you don't reference the reply object to act accordingly.
You can catch those errors with, but the reply cannot be fulfilled:
process.on('unhandledRejection', (err) => {
console.log(err)
})
As an alternative, I would set connectionTimeout, so the client will get a timeout error at least.
As you wrote, you already know that you should change the handler code to let Fastify be aware of the promise:
// add return
app.get('/', (request, response) => {
return fetch('https://...').then(() => { response.send(); });
})
For this reason, I think the solution to your problem should be taken offline adding a linter rule (like so) and integrate some Static Code Analysis in the CI pipeline to reject bad code.

Using await / async with mocha, chai

I'm quite new to node and express.
And have been trying to write test code using mocha, chai and chai-http.
Here's the part of source code.
const mongoose = require('mongoose'),
User = require('../../models/user');
const mongoUrl = 'mongodb://xxxxxxxxxxx';
describe('/test', function() {
before('connect', function() {
return mongoose.createConnection(mongoUrl);
});
beforeEach(async function(done) {
try {
await User.remove({}); // <-- This doesn't work
chai.request('http://localhost:3000')
.post('/api/test')
.send(something)
.end((err, res) => {
if (err) return done(err);
done();
});
} catch (error) {
done(error);
}
});
});
And I get the following error with "npm test"(nyc mocha --timeout 10000 test/**/*.js).
Error: Timeout of 10000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
I confirmed the database connection works properly from log.
And seems I get the timeout error with await User.remove({}).
I've also tried different methods such as a User.save()
But, I got the same error.
Do I need to do something special with database model and connection?
This is all pretty simple.
To avoid the error you must not use both done and async/await in Mocha at the same time. Either use async/await and remove both done as function parameter and done() call. Or use done. Then remove both async/await. See the example tests below for each case.
Use try/catch with async/await as you would normally use it with synchronous code.
Following are the most basic Mocha tests with both async/await and done approaches testing the same basic HTTP server endpoint.
This is async/await approach.
it('with async/await', async function() {
const res = await chai.request(server)
.get('/')
.send();
assert.equal(res.status, 200);
});
This is done approach.
it('with done & callbacks', (done) => {
chai.request(server)
.get('/')
.end((err, res) => {
assert.equal(res.status, 200);
done();
});
});
See the full test file snippet.
For working example additionally spin basic Express server as the tests counterpart in src/app.js.
See Chai HTTP plugin docs for more info on what you can do with request testing.
This is it.
I had the same problem and have not found a way to get any promises that involve mongoose working with Mocha/Chai.
What may help you is doing what I did and putting your mongoose code in a script so you can run it with node <scriptfile>.js. You can use that to confirm it's working properly by itself. In my test, the mongoose operation finished in less than a second. You can also call that file from another (non-test related) to confirm it executes properly and returns a promise. You can see from my example how to make sure you close db properly. Partial example:
...
db.close();
return new Promise((resolve) => {
db.on('disconnected', () => {
console.log('***************************************Mongoose CONNECTION TERMINATED');
resolve('user ready');
});
});
...
You may also find some clues by looking at the following issues here and here.
The work around that I did after wasting too much time trying to figure out this crazy behavior was to perform my mongoose needs in a route. I wrap each request that needs to use it in the end block of the extra chai.request... or use async. Example:
describe('something', () => {
it('should do something and change it back', async () => {
try {
// change user password
let re1 = await chai.request(app)
.post('/users/edit')
.set('authorization', `Bearer ${token}`)
.send({
username: 'user#domain.com',
password: 'password6',
});
expect(re1.statusCode).to.equal(200);
// change password back since before hook not working
let re2 = await chai.request(app)
.post('/users/edit')
.set('authorization', `Bearer ${token}`)
.send({
username: 'user#domain.com',
password: 'password6',
passwordNew: 'password',
passwordConfirm: 'password',
});
expect(re2.statusCode).to.equal(200);
} catch (error) {
// error stuff here
}
});
Note that using the try/catch syntax above will cause test that should normally fail to show passing and the results will be caught in the catch block. If you want to avoid that, just remove the try/catch.
How did you implement ./models/user? await only works if User.remove() returns a promise, not if it expects a callback. I would add debug information to your User.remove() function to see where it gets stuck.

How can I use nock.js to mock node-webshot requests?

What is proper way to mock requests sent by node Webshot during a test using nock.js?
I tried following code to capture mocked response of http://example.com/foo.html as foo.png but the mock doesn't seem to work.
const nock = require("nock");
const webshot = require("webshot");
describe("mock webshot request", function(){
this.timeout(20000);
beforeEach(function(){
nock("http://example.com").persist().get("/foo.html").reply(200, "<h1>Foo</h1>");
});
afterEach(function () {
nock.cleanAll();
});
it("captures mocked response", function(done){
webshot("http://example.com/foo.html", "foo.png",function(err) {
nock.isDone();
if(!err) done();
});
});
});
Edit:
The solution was to pass mocked response body to Webshot rather than url:
webshot("<h1>Foo</h1>", ...
Nock expects the http request to happen in the same process.
Note: node-webshot is a wrapper for PhantonJS which run in another process.
In your case Nock is setup in one process, but the http request happens in another process. So you cannot mock http request done by node-webshot like the way your are currently doing.
What you need is support for mocking http request built into node-webshot i.e you will have to add this feature to node-webshot if it doesn't have it.

mocking server for SSR react app e2e tests with cypress.io

I'm building a single page web application (SPA) with server side rendering (SSR).
We have a node backend API which is called both from the node server during SSR and from the browser after initial rendering.
I want to write e2e tests that configures API responses (like with nock) and work both with browser calls and SSR server calls. some pseudo-code :
it('loads some page (SSR mode)', () => {
mockAPI.response('/some-path', {text: "some text"}); // here i configure the mock server response
browser.load('/some-other-page'); // hit server for SSR response
expect(myPage).toContain('some text');
})
it('loads some other page (SPA mode)', () => {
mockAPI.response('/some-path', {text: "some other text"}); // here i configure another response for the same call
browser.click('#some-link'); // loads another page client side only : no SSR here
expect(myPage).toContain('some other text');
})
Currently Cypress allows me to mock fetch on the browser but not on the server.
Is there anything to achieve that ?
Preferably with node libs.
MockTTP can do that. Excerpt from the doc :
const superagent = require("superagent");
const mockServer = require("mockttp").getLocal();
describe("Mockttp", () => {
// Start your server
beforeEach(() => mockServer.start(8080));
afterEach(() => mockServer.stop());
it("lets you mock requests, and assert on the results", () =>
// Mock your endpoints
mockServer.get("/mocked-path").thenReply(200, "A mocked response")
.then(() => {
// Make a request
return superagent.get("http://localhost:8080/mocked-path");
}).then(() => {
// Assert on the results
expect(response.text).to.equal("A mocked response");
});
);
});
We used a particularly ugly solution, that breaks the speed of cypress, but we needed that in order to mock/fake socket calls.
You can make a real simple express server that starts before running your tests. This 'real fake server' will be able to respond what you need.
Here are the specs of our:
POST on / with method, path and {data} in body params in order to setup a route
GET/POST/PUT/DELETE on /path responds {data}
DELETE on / clear all the routes
Let's consider your 'real fake server' run on 0.0.0.0:3000; you'll do:
beforeEach() {
cy.request('DELETE', 'http://0.0.0.0:3000/');
}
it('loads some page (SSR mode)', () => {
cy.request('POST', 'http://0.0.0.0:3000/', {
method: 'GET',
path: '/some-path',
data: {text: "some other text"}
}) // here i tell my 'real fake server' to
// respond {text: "some other text"} when it receives GET request on /some-path
browser.load('/some-other-page'); // hit server for SSR response
expect(myPage).toContain('some text');
})
it('loads some other page (SPA mode)', () => {
cy.request('POST', 'http://0.0.0.0:3000/', {
method: 'GET',
path: '/some-path',
data: {text: "some other text"}
}); // here i configure another response for the same call
browser.click('#some-link'); // loads another page client side only : no SSR here
expect(myPage).toContain('some other text');
})
Important : the resquests need to be in localhost. You won't be able to stub something external. (Hence, make an env var in order to request localhost:xxxx instead of google.com when you test your app)
You won't be able to control the 'real fake server' otherwise than this cy.request because your tests scripts run in the browser (correct me if I'm wrong) and the browser can't run an express server.

How to test an external HTTP resource using mocha and requestify

I am trying to do a simple test of an external HTTP resource using mocha. Here is my code:
describe('bing.com', function() {
it('should return 200 for a GET request', function() {
var requestify = require('requestify');
requestify.get('http://bing.com')
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
console.log(response.getBody());
done();
});
});
});
The test just says passed but my console.log call is never shown. Is mocha completing before the http response is received?
You don't provide the done callback to your test function:
describe('bing.com', function() {
it('should return 200 for a GET request', function(done) {
...
To catch errors like that you should check your Code with JSHint (or Jslint). Both would inform you that your done() call won't work as the variable is not defined.

Resources