How to hit axios request from Jest test page - node.js

I am trying to hit an axios request from my code.test.js file here:
import axios from 'axios'
import sinon from 'sinon';
describe('get-data', () => {
let data = {start_date:"2017-06-30",end_date:"2017-07-07",graph:"all"}
let sandbox;
let server;
beforeEach(() => {
sandbox = sinon.sandbox.create();
server = sandbox.useFakeServer();
});
afterEach(() => {
server.restore();
sandbox.restore();
});
it('should display a blankslate', (done) => {
axios.get('/api/get/data?data='+JSON.stringify(data))
.then((response.data) => {
console.log(response)
/*expect($('#users').innerHTML)
.to.equal('The list is empty.')*/ })
.then(done, done);
setTimeout(() => server.respond([200,
{ 'Content-Type': 'application/json' },
'[]']), 0);
});
})
But I get console.log(response.data) as undefined.
Can anyone tell me how to get data here in response ?

Technically in your tests you shouldn't be actually doing a request, but rather mocking it and testing side effects.
However if I remember correctly jest mocks everything by default unless told not too. In your package.json add the following section and contents:
{
"jest": {
"unmockedModulePathPatterns": [
"axios",
]
}
}
This should allow you to do your actual network request as necessary in the test. You could also pass the "automock": true, property into the jest section if you wanted to disable automocking.
Documentation:
https://facebook.github.io/jest/docs/configuration.html#automock-boolean
https://facebook.github.io/jest/docs/configuration.html#unmockedmodulepathpatterns-array-string

Related

How to unit test Request library with mocha sinon stub?

How would I test the NPM Request library with Mocha, sinon and chai?
I get an Error: getaddrinfo ENOTFOUND. The URL shouldnt matter as I expect the yields value to return no matter what the url
describe(`api tests`, () => {
it(`should return`, async () => {
sinon.stub(request, `get`).yields(null, null, JSON.stringify({test: `teststub`}))
return apiFunction.then(res => {
assert.equal(res.body, {test: "stubtest"})
})
})
})
const apiFunction () => {
request(
{
url: `http://url`
},
(err, response, body) => {
console.log(body) // should be {test: "subtest"}
})
}
So the answer was that sinon is unable to stub standalone functions, it can only stubs functions where there is a parent e.g. Object.function.
Can use rewire proxyquire however using Jest test runner instead of mocha feels like a better solution as jest provides more complete out the box functionality which includes mocks and assertions

In Nock, URL seems ok to me but is not matched

I am trying to use nock to intercept a call from my app to the internet.
The goal here is to avoid using a variable external API when testing.
What I do is :
describe('My awesome test', () => {
beforeEach(() => {
let scope = nock('http://www.myexternalapi.eu')
.log(console.log)
.post('/my/awesome/path')
.query(true)
.reply(200, response);
console.error('active mocks: %j', scope.activeMocks())
});
it('Should try to call my API but return always the same stuff ', () =>{
myService.doStuffWithAHttpRequest('value', (success) => {
// The answer must always be the same !
console.log(success);
});
})
// Other tests...
}
and myService.doStuffWithAHttpRequest('value', (success) is something like that :
const body = "mybodyvalues";
const options = {
hostname: 'myexternalapi.eu',
path: '/my/awesome/path',
method: 'POST',
headers: {
'Content-Type': 'application/xml'
}
};
const request = http.request(options, (response) => {
let body = "";
response.setEncoding('utf8');
response.on('data', data => {
body += data;
});
response.on('end', () => {
parser.parseString(body, (error, result) => {
// Do a lot of cool stuff
onSuccess(aVarFromAllTheCoolStuff);
});
});
});
When runing my tests, nock display this :
active mocks: ["POST http://www.myexternalapi.eu:80/my/awesome/path/"]
Seems good ! But my request is not matched and the external API is always called !
I have tried :
beforeEach(() => {
let scope = nock('http://www.myexternalapi.eu/my/awesome/path')
.log(console.log)
.post('/')
.query(true)
.reply(200, response);
console.error('active mocks: %j', scope.activeMocks())
});
It don't work neither.
beforeEach(() => {
let scope = nock('myexternalapi.eu')
.log(console.log)
.post('/my/awesome/path')
.query(true)
.reply(200, response);
console.error('active mocks: %j', scope.activeMocks())
});
It don't work neither and display a weird URL :
active mocks: ["POST null//null:443myexternalapi.eu:80/my/awesome/path/"]
Plus something is weird :
Nock can log matches if you pass in a log function like this:
.log(console.log)
Do not display anything... ?! Any idea ?
Thanks you, I'm going crazy with this...
The values you're providing to nock and post are not quite right.
Try this.
let scope = nock('http://www.myexternalapi.eu')
.log(console.log)
.post('/my/awesome/path')
.reply(200, response);
The string argument passed to nock must be the origin, host and protocol, and must not include any path or search param/query info.
Likewise, the post method should receive the path of the call.
One helpful tool when trying to determine why Nock isn't matching a request is to debug which Nock has integrated. So however you're running your tests, prepend DEBUG=nock*.

How to test a Server Sent Events (SSE) route in NodeJS?

I have a Server Sent Events route on my NodeJS app that clients can subscribe to for getting real-time updates from the server. It looks like follows:
router.get('/updates', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
})
const triggered = (info) => {
res.write(`\ndata: ${JSON.stringify(info)}\n\n`)
}
eventEmitter.addListener(constants.events.TRIGGERED, triggered)
req.on('close', () => {
eventEmitter.removeListener(constants.events.TRIGGERED, triggered)
})
})
Testing a traditional route using supertest is simple enough in node:
test('Should get and render view', async() => {
const res = await request(app)
.get('/')
.expect(200)
expect(res.text).not.toBeUndefined()
})
However, this does not work when testing a SSE route.
Does anyone have any ideas on how to test a SSE route with Node? It doesn't necessarily have to be tested with supertest. Just looking for ideas on how to test it, supertest or otherwise.
EDIT:
I have an idea about how to integration test this. Basically, one would have to spin up a server before the test, subscribe to it during the test and close it after the test. However, it doesn't work as expected in Jest when I use beforeEach() and afterEach() to spin up a server.
I would mock/fake everything used by the endpoint, and check if the endpoint executes in the right order with the correct variables. First, I would declare trigger function and close event callback outside of the endpoint so that I could test them directly. Second, I would eliminate all global references in all functions in favor of function parameters:
let triggered = (res) => (info) => {
res.write(`\ndata: ${JSON.stringify(info)}\n\n`);
}
let onCloseHandler = (eventEmitter, constants, triggered, res) => () => {
eventEmitter.removeListener(constants.events.TRIGGERED, triggered(res));
}
let updatesHandler = (eventEmitter, constants, triggered) => (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
eventEmitter.addListener(constants.events.TRIGGERED, triggered(res));
req.on('close', onCloseHandler(eventEmitter, constants, triggered, res));
};
router.get('/updates', updatesHandler(eventEmitter, constants, triggered));
With this code, the test cases would be like:
test("triggered", () => {
let res;
beforeEach(() => {
res = generateFakeRespone();
});
it("should execute res.write with the correct variable", () => {
trigger(res)("whatever");
expect(res.write).to.have.been.called.once;
expect(res.write).to.have.been.called.with(`\ndata: ${JSON.stringify("whatever")}\n\n`);
});
});
test("onCloseHandler", () => {
let res;
let eventEmitter;
let constants;
let triggered;
beforeEach(() => {
res = Math.random();
eventEmitter = generateFakeEventEmitter();
constants = generateFakeConstants();
triggered = generateFakeTriggered();
});
it("should execute eventEmitter.removeListener", () => {
onCloseHandler(eventEmitter, constants, triggered, res);
expect(eventEmitter.removeListener).to.have.been.called.once;
expect(eventEmitter.removeListener).to.have.been.called.with(/*...*/)
});
});
test("updatesHandler", () => {
beforeEach(() => {
req = generateFakeRequest();
res = generateFakeRespone();
eventEmitter = generateFakeEventEmitter();
constants = generateFakeConstants();
triggered = generateFakeTriggered();
});
it("should execute res.writeHead", () => {
updatesHandler(eventEmitter, constants, triggered)(req, res);
expect(res.writeHead).to.have.been.called.once;
expect(res.writeHead).to.have.been.called.with(/*...*/)
});
it("should execute req.on", () => {
//...
});
// more tests ...
});
With this style of coding and testing, you have the ability to make very detailed unit test. The downside is that it take much more effort to test everything properly.
Have a look at the tests for the express-sse library. They spin up the server on a port, then create an instance of EventSource and connect it to the SSE end-point on that running server.
Something like this:
describe("GET /my-events", () => {
let events
let server
beforeEach(function (done) {
events = new EventEmitter()
const app = createMyApp(events)
server = app.listen(3000, done)
})
afterEach(function (done) {
server.close(done)
})
it('should send events', done => {
const es = new EventSource('http://localhost:3000/my-events')
events.emit('test', 'test message')
es.onmessage = e => {
assertThat(e.data, equalTo('test message'))
es.close()
done()
}
})
})
That seems like the right way to test it, to me.

Async Request to API return undefined in Jest

I am quiet new to testing, and specifically to Jest.
I am following several tutorials in which they handle asynchronous code in the manner I am attempting. My code seems to work when I am making a custom Promise that resolves with dummy data. But when I try to use axios to fetch from an external API, Jest gets as a response undefined.
// functions2.js
const axios = require("axios")
const fetch = () => {
axios.get("https://jsonplaceholder.typicode.com/users")
.then(res => res.data)
.catch(err => err);
}
module.exports = fetch;
// functions2.test.js
describe("async operation", ()=>{
it("should be defined", ()=>{
expect(fetch).toBeDefined()
}); // Passed
it("should fetch", async () => {
expect.assertions(1);
const data = await fetch();
expect(data).toBeTruthy();
}) // Did not pass, data is undefined
it("should fetch, using promises", () => {
expect.assertions(1);
return fetch().then(data => {
expect(data).toBeTruthy();
}) // Did not pass, got 0 assertions
})
})
In one tutorial I encountered that this has something to do with Jest running through Node.JS, but I don't know how to handle it because I don't know node.js.
Also, I followed a tutorial by Traversy Media, cloned his Git repo (https://github.com/bradtraversy/jest_testing_basics) and had the same problem (though in the video it worked)
The problem is because you are not returning the promise from fetch.
Update your functions2.js to something like:
const fetch = async () => {
return axios
.get("https://jsonplaceholder.typicode.com/users")
.then(res => res.data)
.catch(err => err);
};

nock is not intercepting my request

I'm trying to create some basic tests using karma server and nock.
It seems like nock is not intercepting my requests at all, does anyone have idea? I can't figure out what is missing. I still getting real data.
nock('https://api.github.com/users/' + username).log(console.log)
.get('/')
.query(true)
.reply(400, {
statusMessage: 'Bad Request',
foo: 'foo'
})
http.get('https://api.github.com/users/' + username, function(res) {
console.log('res', res)
})
I also added this middleware
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
====== UPDATE Jun 6 ======
Whole flow using react-redux
Here is my test:
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import axios from 'axios';
import expect from 'expect';
import * as actions from 'actions/test-actions'
import * as types from 'types';
import nock from 'nock'
import { username } from 'constansts'
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
describe('Asynchronous actions', () => {
it('Basic example', done => {
nock('https://api.github.com')
.get('/users/' + username)
.reply(400, {
statusMessage: 'Bad Request',
foo: 'foo'
})
var expectedActions = []
let store = mockStore([], expectedActions, done)
store.dispatch(actions.testRequest())
.then(() => {
console.log('store.getActions() => ', store.getActions())
})
.then(done).catch((err) => {
console.log('ERROR==>', err)
done()
})
})
})
And here is the action
export function testRequest () {
return axios.get('https://api.github.com/users/' + username)
.then(function (res) {
console.log('response =>', res.status)
})
.catch(function (err) {
console.log('error =>', err)
})
}
res.status is 200, even if I use nock for changing to 400
This is an old question but I believe the answer is that you need to set the axios http adapter:
import axios from 'axios';
axios.defaults.adapter = require('axios/lib/adapters/http');
When running tests with jest you generally run them in a "browser like" environment. To get axios to use the node http library instead you need to specifically tell it to use the http adapter.
https://github.com/axios/axios/tree/master/lib/adapters
You should specify the path in the get method:
nock('https://api.github.com').log(console.log)
.get('/users/' + username)
.query(true)
.reply(400, {
statusMessage: 'Bad Request',
foo: 'foo'
});
Are you running your tests in a node environment or in a web browser (like PhantomJS)?
In order to use nock you must run your tests in node (using Jest or mocha), nock overrides node http behavior and for that reason it only works in node and not in browsers (like PhantomJS).
To have your test running you can:
run it in a node environment (like Jest or mocha)
or use a different library to mock like fetch-mock
I found the answer!
Aparently there is no compatibility with axios, I also tried with 'fetch', 'isomorphic-fetch' but no luck.
'whatwg-fetch' was the answer
Thanks very much and I hope this post helps someone else.
import 'whatwg-fetch'
export function testRequest () {
return fetch('https://api.github.com/users/' + username)
.then(function (res) {
console.log('response =>', res.status)
})
.catch(function (err) {
console.log('error =>', err)
})
}

Resources