how to stub request.get three times? with 2 same url and 1 different url? - request

I need to write unit testcase for following piece of code. Here urls are external api calls, so I need to mock these. For rest of the testcases I have used sinon. So would like to write these in sinon only.
function 1 {
async.waterfall() {
function 2();
function 3();
}
}
function 2(callback) {
url2_1 = "/calculatorFunctionList"
request.get(url2_1,function (err, res, body) {
return callback("with some diff response")
})}
function 3(callback) {
url3_1 = "addition/values="+somevalue1
url3_2 = "addition/values="+somevalue2
request.get(url3_1,function (err, res, body) {
if(!err) {
request.get(url3_2, function (err, res, body) {
return callback("with some response")
})
}
})
}
I need to stub these requests. How can I do this? Also how and when to call restore for stub so that it won't mess up with other request stub? Currently both stubs are not getting mocked.

Sinon - at its core (forget lolex and nise for a moment) - explicitly deals with stubbing and spying on javascript functions. That's it. It does not concern itself with module loaders or Node per se.
So it does not know how to hook into Node's module loader (require) and stub out dependencies by itself. For the general case, you have two options:
Supply your module (the System-Under-Test) with injection points (say, a function setRequestModule(module){ request = module }, which could be used to supply a Sinon stub.
Use a link-level to intercept calls to the module system to load dependencies and swap them out. See this article on Sinon's how-to section for how to do this.
That was the general case, but since you are dealing with network level stubbing, there is a third option, which is related to the second one, which involves hooking into the http layer in Node and setting up fake requests and responses there using nock.
This would look like this:
const nock = require('nock')
const scope = nock('https://api.github.com')
.get('/repos/atom/atom/license')
.reply(200, {
license: {
key: 'mit',
name: 'MIT License',
spdx_id: 'MIT',
url: 'https://api.github.com/licenses/mit',
node_id: 'MDc6TGljZW5zZTEz',
},
})
This is what I would normally do, but since you insist on only using Sinon you are left with the first option.
Elaboration of the first option: inject dependencies
Your functions 2 and 3 (invalid javascript identifiers, btw) are both dependant on the module request and its method get to have their results. To be able to control what is returned by request.get you would then need to supply your module with a different version of request for your tests. This can be done using dependency injection.
To start with, I assume your function starts with an import of the request module, meaning the first few lines includes a statement looking like this:
const request = require('request');
You would need to change this statement so that the request constant can be replaced, meaning it would now look like this:
let request = require('request');
To be able to control your dependency you can now add a new function to your exports:
module.exports.setRequestModule = function (stub){
request = stub;
}
To use this in your tests you would then need to do this in your tests:
const myModule = require('../src/my-module');
const myRequestStub = {};
myModule.setRequestModule(myRequestStub);
it('should return the expected response', () => {
const expectedOutput = "with some response";
myRequestStub.get = function (url, callback) {
callback(null, expectedOutput);
}
myModule.doSomething() ...
// do test
});
Alternatively
You could also expose the request object in your module:
module.exports._request = request;
You could then, for instance, use sinon to instrument it by stubbing the get method from your tests.
const myModule = require('../src/my-module');
sinon.stub(myModule._request, 'get').callsFake( (url, cb) => cb("hello!") );

Related

Modify every data response in strapi

Im wondering what the best way to execute a function before the any data is returned from the api so that i can modify the data response.
I know that i could add the function to every single controller however this would mean repeating myself a lot of times. Policies seemed like the correct thing to do however these will execute before the controller is hit.
Anyone know how to do this?
module.exports = {
async find(ctx) {
let entities;
ctx.resp... // altering the data this way at the moment
A policy can be made to execute after the controller function has been executed the code has to come after the await next() as shown below.
module.exports = async (ctx, next) => {
// Indicate to the server to go to
// the next policy or to the controller's action.
await next();
// The code below will be executed after the controller's action.
if (ctx.status === 404) {
ctx.body = 'We cannot find the resource.';
}
};
kindly refer :Advanced usage

How to make assertions on graphql query variables when using Mock Service Worker?

When we are mocking out a graphql query with a mock service worker (MSW), we want to assert that the variables passed to the query have certain values. This goes beyond the type validation with the typescript typings. We are using jest with MSW. Do you spy on MSW to make those assertions? or is there another way to expect req.variables to have a certain value.
graphql.query<SaveContent, SaveContentVariables>('SaveContent', (req, res, ctx) => {
return res(
ctx.data({
saveContent: {
success: true,
id: req.variables.id,
errors: [],
},
})
);
})
Mock Service Worker recommends basing your request assertions on the UI (read more in the Request assertions recipe). In most cases, if your request/response data is correct, then your UI would be correct in the test. The same is true for the opposite scenario. Always assert the data-driven UI, when you can.
In your case, you wish to assert the query variables in a request. Consider returning data based on those variables that later result in a corresponding UI.
When you find it absolutely necessary to perform direct request/response assertions apart from the UI, use the Life-cycle events that allow executing arbitrary logic in response to various MSW events. For example, this is how you can assert request variables in your test:
const server = setupServer(...handlers)
it('saves the content', async () => {
expect.assertions(1)
server.on('request:match', (req) => {
expect(req.variables).toEqual({ id: 'abc-123' })
})
await performQuery(...)
})

When attempting to stub a request in Cypress IO (JS) against a React app using fetch, the requests still call through to the server

I'm using Cypress to create some specs against my React app. My react app uses fetch to fetch data from an external api (isomorphic-fetch)
The fetch requests in my app are like so
import fetch from 'fetch'
...
fetch('http://www.external-server.com/ideas.json')
.then((response) => {
if (response.status >= 400) {
}
return response.json().then((result) => {
this._data = result
this._data((ele) => ele.key = ele.id)
});
})
In my Cypress specs, I want my regular specs to hit my lcoahost:3000 to get the initial page (which houses my React app). My react app in turn would normally make an external request (http://www.external-server.com/ideas.json) but in my specs I want to stub out that request and have that endpoint return fake data in my specs only.
The Cypress docs for cy.route() here, describe that I should be able to do something like
cy.server()
cy.route('http://www.external-server.com/ideas.json', [
{
id: 1,
name: 'john'
}
])
I attempted to put this into a beforeEach that runs in the context of my spec (thus running before every spec).
You will note that when I run the specs in the Cypress test running, it appears in the console output that the endpoint SHOULD be stubbed.
However, by examination, I can see that my app is in fact making the request to the real server, and calling the real endpoint (not stubbing it).
I tested several times and I am certain this is the behavior.
I found a solution online I will post below to answer my question
the solution is add to cypress/support/commands.js
this small hack will turn the window fetch into a no-op (disabling it) and will allow the native stubbing in Cypress to work without any alterations.
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
const opts = Object.assign({}, options = {}, {
onBeforeLoad: (window, ...args) => {
window.fetch = null;
if (options.onBeforeLoad) {
return options.onBeforeLoad(window, ...args);
}
},
});
return originalFn(url, opts);
});

How do I create a globally accessible variable in React JS

In my react JS app, I make many API calls,
Rather than having to specify:
const BASE_URL = 'https://apiurlgoeshere.com/'
on every page, I'd rather have BASE_URL accessible throughout the entire application, so I can just do BASE_URL + API_CALL for example
If this is just adding BASE_URL, then this can be achieved by declaring it inside a constants.js file and exporting it from there. But then, that makes us do BASE_URL + "something" each time we make a network request which isn't really ideal either. Also there might be some scenarios where other configuration have to be shared, like say, a common header that has to be added to all the requests.
To solve this, most request libraries have in-build solutions. If we are choosing axios as the most popular one, we can create a instance like:
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
export default instance;
and import this everywhere the axios is going to be used like:
import axios from "./axios-instance";
assuming axios-instance.js is the file where the instance is created. Now you can skip adding the BASE_URL to every request as it is already provided in the instance.
If webpack is being used for code bundle, DefinePlugin can be used.
new webpack.DefinePlugin({
'BASE_URL': JSON.stringify('https://apiurlgoeshere.com/')
});
For gulp build, gulp-replace can be used.
.pipe(replace('BASE_URL', 'https://apiurlgoeshere.com/'))
I know it's been a while since I created this post - just wanted to go through what I've learnt really.
It's a great way to set a global config for Axios. I typically create a folder and create an api.js file within it which I use to make all of my API calls, this is great as it means you only have to specify headers / base URL / credentials etc once.
Here is a code example for a solution:
function apiCall (method, path, data) {
let url = "https://urltoyourapis.com/";
return new Promise((resolve, reject) => {
return Axios[method](url, {headers}, data).then(res => {
return resolve(res);
}).catch(err => {
return reject(err);
})
})
}
Now, whenever you want to make an API call you'd import this function into the file where you would like to make the API call and do the following:
apiCall("get", "account-info")
This will then make an API call to the endpoint "account-info" to get the information, you can either await and set the result to a variable or use .then .catch to handle the response.

Capture server JSON response in end-to-end test

I'm writing an end-to-end test that simulates user authentication with Protractor. A user feels in her credentials and clicks a Submit button. As a result, the server returns an access token in a JSON response that can be used for other REST API calls. I'd like to save this token to a file.
There's a similar question on capturing a response of a GET request here, but I'm not sure it's a good idea to send another request after I click the button.
How can I capture the response after a button click?
Here is my idea about how to catch HTTP responses. Protractor provides a method browser.addMockModule() (docs) - it is used to add custom Angular modules to a page, which are usually used to mock outcoming requests and provide custom response. But we do not need to mock requests, it would be enough to just listen for whatever comes from a server. It can be achieved with the help of Angular HTTP interceptors. Interceptors are used to catch a request or a response and modify it for whatever needs before in gets to it's endpoint. We can use them to collect information about what is coming from the server, store it, and then let response go forward without changes. Since this custom module and spec tests will run on the same page, information about responses can be stored in some global property. Then, when button is clicked, it would be possible to inject custom script to a page to retrieve required responses via browser.executeScript() (docs). Here is the source:
it('should intercept requests', function () {
// Inject custom Angular module on a page
// Script should be injected before you "browser.get()" the page
browser.addMockModule('e2eHttp', function () {
// Note: This function scope is not a Protractor environment
angular
.module('e2eHttp', [])
.config(function ($httpProvider) {
// add custom interceptor to all requests
$httpProvider.interceptors.push('e2eHttpInterceptor');
})
.factory('e2eHttpInterceptor', function () {
return {
response: function (response) {
// name of the global property to store responses
var global = 'e2eHttpResponses';
// responses will be grouped by url
// but you can use data from "response.config" to adapt it
// it has a lot of info about response headers, method, etc
var url = response.config.url;
window[global] = window[global] || {};
window[global][url] = window[global][url] || [];
window[global][url].push(response); // store response
// proceed without changing response
return response;
}
};
});
});
// Load the page
browser.get('#/auth/login');
$('#submit').click();
// If we are sure that response has come, then extract it
browser.executeScript(function () {
// Note: This function scope is not a Protractor environment
var global = 'e2eHttpResponses';
var uri = 'api/auth/login.json';
// extract array of stored responses for required uri
var responses = (window[global] && window[global][uri]) || [];
// return responses to spec
return responses;
}).then(function (responses) {
// and finally, we are now able to get all information we need
// about response, and in your case, save it to a file
console.log(responses);
var data = responses[0].data; // first response body as a string
});
// remove injected module not to break another specs
browser.removeMockModule('e2eHttp');
});
You can move setup and injection calls to some utility modules, so test specs would be clean.

Resources