Testing basic async http request in Node with Got, Nock & Chai - javascript

I am trying to figure out why my unit test is not working correctly. It seems that the external network request is made despite my using Nock to intercept my http request.
I have a very basic getUser service, getuser-got.js:
const got = require('got');
module.exports = {
getUser(user) {
return got(`https://api.github.com/users/${user}`)
.then(response=>JSON.parse(response.body))
.catch(error => console.log(error.response.body))
}
};
This can be called succesfully but I want a unit test for it.
Here's my code in a file named getuser-got.test.js:
const getUser = require('../getuser-got').getUser;
const expect = require('chai').expect;
const nock = require('nock');
const user_response = require('./response');
describe('GetUser-Got', () => {
beforeEach(() => {
nock('https//api.github.com')
.get('/users/octocat')
.reply(200, user_response);
});
it('Get a user by username', () => {
return getUser('octocat')
.then(user_response => {
// expect an object back
expect(typeof user_response).to.equal('object');
// test result of name and location for the response
expect(user_response.name).to.equal('The Octocat')
expect(user_response.location).to.equal('San Francisco')
})
});
});
The file named response contains a copy of the expected response from the Github API, which I am loading into the user_response variable. I have replaced the values for name and location in order to make my test fail.
module.exports = {
login: 'octocat',
...
name: 'The FooBar',
company: '#github',
blog: 'https://github.blog',
location: 'Ssjcbsjdhv',
...
}
The problem is that I can see that Nock is not intercepting my request. When I run the test it continues to make an actual call to the external API. The test therefore passes, because it is not using my local response as the return value.
I've tried adding in nock.disableNetConnect(); but this just causes the test to timeout, as it's clearly still trying to make the external call. If I run my test I get:
➜ nock-tests npm test
> nock-tests#1.0.0 test /Users/corin/Projects/nock-tests
> mocha "test/test-getuser-got.js"
GetUser-Got
✓ Get a user by username (290ms)
1 passing (296ms)
What am I doing wrong to not have Nock intercept my http request?

The value being passed to the nock function is not a valid URL, it's missing the colon in the schema.
Updating it to nock('https://api.github.com') gets the test to fail locally, as desired.

Related

How to read a response in Cypress?

I am trying to write an E2E test using Cypress 12.3 for a web application. During a certain part of the journey, the app makes a GET request to the endpoint "api/v2/accountApplication/getApplicationInfo?uuid=xxxxxxx". The response from this request includes a field called "abChoice.welcome" which has a value of either 'a' or 'b'. This value is used for A/B testing in my Vue app. The structure of the response is as follows:
{
"resultStatus": true,
"errorCode": 0,
"errorMessage": null,
"resultData": {
"abChoice": {
"welcome": "a"
}
}
}
I am trying to write a test that checks the response from this request and makes different assertions based on the value of "abChoice.welcome". I have attempted to use the cy.intercept command to check the response, but it is not working as expected. I also tried creating an alias for the request and using cy.wait(#myAliasName), but Cypress threw an error and said the request was never made, even though I can see the request in the logs.
describe('A/B testing', () => {
it('shows A or B pages', () => {
cy.intercept('GET', '**/accountApplication/getApplicationInfo', req => {
const { body } = req
cy.log(body)
if (body.resultData.abChoice.wlecome === 'a') {
cy.log('A')
// assert something
} else {
cy.log('B')
// assert something
}
})
})
})
The log shows the following, so the request is definitely being made. (I've removed sensitive information)
(xhr)GET 200 /api/v2/xxxx/accountApplication/getApplicationInfo?uuid=xxxx
You may have the same issue as here Cypress intercept() fails when the network call has parameters with '/'.
In fact it's not "/" that causes the issue but the parameter section (anything after ?), it would be considered as part of the URL.
As per reference question, use a regex or pathname instead of url (default).
cy.intercept('GET', /\/accountApplication\/getApplicationInfo/, req => {
or with pathname, parameters are excluded from the match
cy.intercept({method:'GET', pathname: `**/accountApplication/getApplicationInfo`}, req => {
The reason your cy.wait() does not succeed and your validation is never run is because your intercept's request url is never matched. This is because your url does not include the query parameters.
Assuming that the query parameter uuid is not relevant to your intercept, you could do something like the following, adding a wildcard onto the end of your existing url:
cy.intercept('GET', '**/accountApplication/getApplicationInfo*', (req) => { ... });
If uuid were relevant, you could simply attach it to the end of url matched on:
cy.intercept('GET', '**/accountApplication/getApplicationInfo?uuid=*', (req) => { ... });
If you weren't assured that uuid was going to be the first query parameter, you would need to update the "simple-string" url method to using a RouteMatcher.
cy.intercept({
pathname: '**/accountApplication/getApplicationInfo'
query: {
uuid: 'foo'
},
method: 'GET'
}, (req) => { ... });

How to export a variable from inside a Mocha describe block

I am currently using Chakram API Testing framework to test some REST API endpoints.
The first API gets a CSRF token which is used in the rest of the endpoints in the headers.
The CSRF API returns a JSON object - something like this
{
csrf_token : Aajkndaknsda99/adjaj29adja
}
This is how I'm doing it right now
describe('Hits the CSRF API to get the token',()=>{
let csrf_tok;
before(()=>{
return chakram.wait(response = chakram.get(API_ENDPOINT,headers));
});
it('gets the csrf token from the response',()=>{
return response.then(function(resp){
csrf_tok = response.body.csrf_token;
console.log(csrf_tok) //works fine and I can see the value correctly
exports.csrf = csrf_tok;
});
});
});
In my other file, where I need to use the CSRF token, I'm doing something like this
var token = require('../test/csrf_token');
var options ={
headers :{
//other headers
CSRF-TOKEN : token.csrf;
}
}
However, this is not working and the rest of the API endpoint tests are failing due to the token being passed as undefined. I hard coded the value of token and then the tests starts working. However, I do not want to do this every time (I plan on deploying this as a part of pipelines).
This issue seems to be that the variable cannot be accessed outside of Mocha's describe context. Is that right? If so, how can I overcome it?
You can declare the variable outside describe and then export it from outside 'describe'.
Other thing I have noticed regarding line:
csrf_tok = response.body.csrf_token;
It should be :
csrf_tok = resp.response.body.csrf_token;
This doesnt answer your specific question, but I needed something similar - where I needed to get an auth token that could then be passed to other tests.
I did this with a before hook in a shared.js file
before ( function getToken (done) {
chai.request(host)
.post(tokenURL)
.send({my params})
.end(function(err, response){
... getToken expectations
this.myToken = response.token;
done();
});
});
Then in test.js file you can just use 'myToken', as long as your shared.js file is in the root test dir
See https://gist.github.com/icirellik/b9968abcecbb9e88dfb2

How to handle backend errors from Node/Koa on frontend apollo-client

My frontend, using apollo-client, throws an exception when the backend returns an error after a request.
When the node server receives a request, I check the validity of the request's token using koa middleware. If the token is valid, the request is forwarded to the next middleware. If the token is invalid, I want to return a 401 access denied error to the client. To do this, I followed Koa's error documentation located here.
The code for the error handling middleware I wrote:
function userIdentifier() {
return async (ctx, next) => {
const token = ctx.request.headers.authorization
try {
const payload = checkToken(token)
ctx.user = {
id: payload.userId,
exp: payload.exp,
iat: payload.iat,
}
} catch (error) {
ctx.user = undefined
ctx.throw(401, "access_denied")
// throw new Error("access_denied")
}
await next()
}
}
This seemingly works on the backend, but not on the frontend. When the frontend receives this error, a JavaScript runtime error occurs. I am not sure what causes this.
Note, the unexpected "a" is the same "a" found in ctx.throw(401, "access_denied"). If it were instead ctx.throw(401, "x") the frontend shows "unexpected token x" instead.
The frontend code where the errors happens:
In an attempt to fix this, I followed Apollo's error handling documentation and used apollo-link-error.
const errorLink = onError(props => {
const { graphQLErrors, networkError } = props
console.log("ON ERROR", props)
if (graphQLErrors)
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
)
if (networkError) console.log(`[Network error]: ${networkError}`)
})
Then I combine all links and create the Apollo client like this:
const link = ApolloLink.from([errorLink, authLink, httpLink])
export const client = new ApolloClient({
link,
cache: new InMemoryCache(),
})
The output of the debugging log in apollo-link-error is as follows:
Related Documents
Someone seems to be having an identical error, but a solution was not listed.
I found that the errors were handled correctly on the frontend when I began using this library on the backend: https://github.com/jeffijoe/koa-respond
Using just ctx.unauthenticated()
But I would still like to know more about how to return json/object-based errors with koa without a plugin helping

stub request library in javascript

I'm using a third party restful service for sending SMS verification code. I wrote an unit test for it. However I dont want to be sent a message each time I run the unit-test.
The code is like:
const _request = require("request");
_request({
method: "POST",
url: "http://blah.com/json",
form: {
apikey: "blah",
mobile: input.mobilePhoneNumber,
text: `code is: ${verificationCode}`,
}
}, (err, res, body) => {
if (err) {
dbg(`end, output=${err}`)
return reject(new Error("something wrong"))
} else {
dbg(`end, output=${res}`)
return resolve({})
}
})
And in the test Im using sinon.stub
sinon.stub(request, "post").returns(Promise.resolve({}))
However this stub doesnt really catch the "post" method in request. I looked into the source code and tried many ways (like stub the constructor instead), but none works.
Wondering if there's anyone tried this before. How shall I stub this post method on request?
Thanks!
Your code is not calling request.post(), so stubbing it won't catch the call you're making.
Instead, you need to stub the request function itself, which is difficult to do with Sinon alone because it requires a "parent" object in which it can replace/stub a method/function.
You can work around that using proxyquire, which you can use to "catch" the import of request and replace it with a stub:
const sinon = require('sinon');
const request = require('request');
const proxyquire = require('proxyquire');
let stub = sinon.stub();
const mod = proxyquire('./your-module', { request : stub });
In your test cases, you can make stub yield to the callback you pass:
let fakeError = null;
let fakeResponse = {};
let fakeBody = '';
stub.reset();
stub.yields(fakeError, fakeResponse, fakeBody);
The arguments to yields represent err, res and body in your code. You can pass various combinations to check if your code handles the responses properly.
The reason for calling stub.reset() is that you need to make sure, before each test, that the stub is reset to its original state (so it starts "fresh").

How to correct this test case in mocha framework?

I am trying to write test case for node application, which is using mocha as test framework.
test.js
var register = require('../routes/users');
var request = require('request');
var baseUrl = 'http://localhost:5000';
describe('registerUser()', function() {
it('check email is already registered', function (done) {
request.post({uri:baseUrl+'/register', form :{
username: 'test',
email: 'test#test.com'
}}, function (e, res, body) {
res.should.have.property('statusCode', 201);
res.should.have.property('regErr', 'This email is already taken!');
})
});
})
The regErr is given as parameter in function registerUser on render. I expected the parameterregErr will be set as property of response and can be get in test.
Please check my registerUser function in github.
I am trying create a test case for this function.
Above code doesn't have property called regErr in response fetched in test case.
How to correct so that the rendering parameters can also be property in response?
Shall I need to change the actual function registerUser to get this? If so, how can I achieve this?
When doing an HTTP request, the callback function is called with three arguments:
err (Possible error return)
res (HTTPRequest object)
body (Buffer of the resulting body)
Therefore, regErr is in the body variable. And since you are rendering an HTML page, you're gonna have to parse the body yourself to find it. A possible solution would be to render JSON and use JSON.parse() on the resulting body buffer.

Categories