How to mock an axios request using sinon modules - javascript

There seems to be so many different ways to do this, but I am trying to use just sinon, sinon-test, chai/mocha, axios, httpmock modules. I am not able to successfully mock a GET call made using axios. I want to be able to mock the response from that axios call so the unit test won't actually have to make the external API request.
I've tried setting up a basic unit test by creating a sandbox, and using sinon stub to set up a GET call and specify the expected response. I'm unfamiliar with JavaScript and NodeJS.
// Main class (filename: info.js)
function GetInfo(req, res) {
axios.get(<url>).then(z => res.send(z.data));
}
// Test class (filename: info.test.js)
it ("should return info", () => {
const expectedResponse = "hello!";
const res = sinon.spy();
const aStub = sinon.stub(axios, "get").resolves(Promise.resolve(expectedResponse));
const req = httpMock.createRequest({method:"get", url:"/GetInfo"});
info.GetInfo(req, res);
// At this point, I need to evaluate the response received (which should be expectedResponse)
assert(res.data, expectedResponse); // data is undefined, res.status is also undefined
// How do I read the response received?
});
I need to know how to read the response that is supposed to be sent back (if it is being captured in the first place by sinon).

I'm assuming the response you're wanting to check is the z.data being passed to res.send(z.data)
I don't think your Sinon Spy is being set up correctly.
In your example, res is a function created by sinon. This function won't have a property data.
You probably want to create a spy like this:
const res = {
send: sinon.spy()
}
This gives you a res object which has a spy with the key send. You can then make assertions about the parameters used to call res.send
it ("should return info", () => {
const expectedResponse = "hello!";
const res = {
send: sinon.spy()
};
const aStub = sinon.stub(axios, "get").resolves(Promise.resolve(expectedResponse));
const req = httpMock.createRequest({method:"get", url:"/GetInfo"});
info.GetInfo(req, res);
// At this point, I need to evaluate the response received (which should be expectedResponse)
assert(res.send.calledWith(expectedResponse)); // data is undefined, res.status is also undefined
});

Dont know if this helps but you may not be getting the correct response because resolves is a return with a promise wrap over it.
So by using resolves and inside it a Promise.resolve you are actually returning Promise wrap in a Promise.
Maybe you can try changing the code to the one below.
const aStub = sinon.stub(axios, "get").resolves(Promise.resolve(expectedResponse));
to
const aStub = sinon.stub(axios, "get").resolves(expectedResponse);

Related

AWS SSM getparameters make behavior node js sync

Is there any way to make AWS SSM getparameters sync?
Requirement :
The secret key, id stored in the SSM store should be accessible on the server up.
Using Node and express in the backend as a reverse proxy, so we have a constant.js file, it stores all the API URLs, paths, etc.
constats.js file
const api1url = 'http://example.com/path1'
const api1_secretkey = 'secret_key'
..................
module.export = {api1url,api1_secretkey}
So we wanted to call the ssm stuff in here before setting the const variables
const SSM = require('aws-sdk/clients/ssm');
const ssm = new SSM();
const params = {
Names: ['/secret_key_api_1', '/secret_key_api_2'],
WithDecryption: true,
};
const parameterList = await ssm.getParameters(params).promise();
I understand that await cant be without async, I just wanted to convey what I am expecting, without it being an async call or whether it be in the callback of getparameters.
const api1url = 'http://example.com/path1'
const api1_secretkey = parameterList.Parameter[1].Value
But since it is an async call we are not able to wait for it,tried doing it in a separate async function and then returning the data but since the function is still async having difficulties.
Not exactly issue wrt to an async function, the async function works as expected, but I want it to be synced, the AWS get parameter function is async. because of this I am not able to do something like const api1_secretkey = process.env.secret_key , this value will be obtained when js file is executed line by line. but since i am waiting for the secret key to come from async func by the time the line by line occurs const api1_secretkey = parameterList.Parameter[1].Value , the promise is still not resolved so undefined will sit. Even if i make a .then on the get params i can make this const api1_secretkey = parameterList.Parameter[1].Value after promise resolved. but since the export module is already done the other file carries undefined in the secret key even after it is changed on promise resolve.

Calling AbortController.abort() on resolved fetch

I have a React app that can makes a series of fetch requests, depending on user interactions. I want to abort old fetch requests any time the app receives a new one.
To accomplish this, I've created a custom hook, useData. Its main fucntion is running a useEffect hook whenever the url changes. The easiest way to abort old requests seems to me to be using the cleanup mechanism provided by useEffect. But this will call abort on all requests, not just incomplete ones.
Are there any hidden problems this might cause? It seems that it shouldn't do much to a resolved fetch, but I can't find any documentation to support that.
This is my code:
/** Returns data for a given url. */
export const useData = function (dataUrl) {
const [loadedData, setLoadedData] = useState(Object.assign({}));
// runs if/when the url changes
useEffect(() => {
// new controller for each new url
const controller = new AbortController();
async function getData(dataUrl) {
if (!dataUrl) return;
// load data & set state
try {
const data = await fetch(dataUrl, {
signal: controller.signal,
});
setLoadedData(data);
} catch (e) {
console.error(`useData failed for url ${dataUrl}\n${e}.`);
}
}
getData(dataUrl);
// clean up the last request before running
// useEffect for on the next url
return () => {
controller.abort();
};
}, [dataUrl]);
// return data in the hook's state, set by useEffect
return loadedData;
};
Fetch returns a promise so, once it resolves, any abortion does not have any effects.
You can run this simple demo in the console, you'll see that the request completes (at least until connection) and no errors are generated:
async function test() {
const controller = new AbortController()
const request = await fetch('https://stackoverflow.com', {signal: controller.signal})
console.log('The request was ok?', request.ok)
controller.abort();
}
test();

Completely replace value of variable in Node.js Unit test

I am trying to test a (to me) complex piece of code that I would like to just return dummy data so I can do assertions on how it is handled. Here is the code:
const { sql, dbConnPoolPromise } = require('../database/db.js');
router.get('/', async (req, res) => {
try {
const pool = await dbConnPoolPromise
const result = await pool.request()
.query(SQL_SELECT_ALL);
res.status(200);
res.json(result.recordset[0]);
} catch (err) {
res.status(500);
res.send("ERROR, General Get" + err.message);
}
});
I want to replace the const result with a set piece of dummy data, thus mocking the response a SQL server might give.
I cannot figure out how to do this.
I am currently using Jest as my testing suite and was hoping to use spyOn or Jest.fn() to replace it but that only works on functions. Is there a way in jest to replace variables? I cannot replace the entire router.get as I am testing that res.status(200) (amongst others) is being sent correctly to the client, I only want to replace the value of the variable.
If required the contents of dbConnPoolPromise are:
let sql = require('mssql');
const config = require('config');
let dbConnPoolPromise = new sql.ConnectionPool(config.get('connection'))
.connect()
.then(pool => {
return pool
})
.catch(err => console.log('Database Connection Failed - error: ', err))
module.exports = {
sql, dbConnPoolPromise, buildSelect
};
You can access your query from a service wrapper, something like.
export function async getAllItems() {
const pool = await dbConnPoolPromise
return await pool.request().query(SQL_SELECT_ALL)
}
Then you can use jest's manual mocks functionality https://jestjs.io/docs/en/manual-mocks to mock out the response of getAllItems.
As a broader comment, this is usually why we have patterns to access DB from a repository or service rather than directly hitting it from a controller or route like this. Once you wrap it in a service, it makes it so much easier for you to mock it out during a test.

How to access a value from within a .then promise?

I am doing the following:
fetch("someurl")
.then(data => {return data.json()})
.then(resp => console.log(resp));
Now, usually i do the operations on resp from within the .then function, but would it be possible to assign resp to a variable, or at least store it somewhere so i can retrieve it in another function?
Example:
let thedata;
fetch(URL).then(res => {
return res.json()
}).then(data => {
console.log(data[0].category); //helloWorld
thedata = data[0].category
});
console.log(thedata);
function printsomething()
{return thedata}
Now thedata is going to be undefined, and i can't use the function printsomething without having it inside the .then() function.
This is what i meant by my question.
By assigning the fetch Promise chain to a variable, you can then call .then on that variable from multiple locations, though that's somewhat odd to do:
const prom = fetch("someurl")
.then(res => res.json());
prom.then((data) => {
console.log(data);
});
// Separately:
prom.then((data) => {
console.log(data.foo);
});
In most cases, it would make more sense to use your original strategy of putting everything inside a single .then after res.json().
Sure you can do that - but then you will need to track whether the async function has returned a value, which may be as simple a checking whether the value is defined. The variable could be in global scope, or a field in some object you import. But by not having the code that requires it called from within the then, it means wrapping use of that variable in a check, and handling the possibility it hasn't been set yet. Whether or not that is a good idea depends on context - best to avoid it if you can, but it's a pattern I have used on occasion.
No matter how you handle it you will need to either check or wait for the value so it's best to use it within the .then()
While it can be done if you need the variable outside of the .then() I think async/await is cleaner/easier to manage the flow by awaiting functions till they finish in the same way that .then would and ensuring your variable is available (you should still validate):
const someAPI = "https://jsonplaceholder.typicode.com/todos/1"
let data = null
const fetchFunc = async () =>{
const response = await fetch(someAPI)
data = await response.json()
// use data
}
const asyncFunc = async () => {
await fetchFunc()
console.log(data);
//use data
}
asyncFunc()

Ajax call in React Starter Kit

I'm using React Starter Kit to create my first React app, and I'm struggling with Ajax calls. I saw that this Kit embeds a way to perform Ajax calls (which is by the way used internally for app routing):
import fetch from '../../core/fetch';
I've added this in my component, and then try to perform an Ajax call when the component loads. Here is my code:
componentDidMount() {
var moduleManager = 'https://my_domain.com/my_endpoint';
async function getModules (url) {
const response = await fetch(url);
const content = await response.json();
return content;
};
this.state.modulesList = getModules(moduleManager);
console.log(this.state.modulesList);
}
I'm also using the state value in my render function:
render() {
var rows = [];
for (var i = 0; i < this.state.modulesList.length; i++) {
rows.push(
<li>{this.state.modulesList[i]}<li/>
);
}
This code put together logs this in my console:
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
Then the Ajax call is performed (successfully) and my console is now showing this:
Promise
__proto__:Promise
[[PromiseStatus]]:"resolved"
[[PromiseValue]]:Array[16]
The desired behaviour is of course to update my view: when the ajax calls is performed, display one line per array member.
What am I missing?
Thanks
What I suggest doing:
constructor() {
...
// Bind your async function to your stateful component's class
this.getModules = this.getModules.bind(this);
}
async getModules(url) {
try {
// Perform the ajax call
const response = await fetch(url);
// Convert respone to json
const content = await response.json();
// Process your content (if needed)
...
// Call setState() here
this.setState({someContent: content});
} catch(e) {
console.error(e);
}
}
componentDidMount() {
this.getModules(`${URL}`);
}
You can't actually return the fetched/parsed data from an async function. According to this MDN link, async function returns a promise, and not the actual data you'd expect to get after parsing it.
What happened in your case, was that you were actually trying to receive a returned value from an async function, inside a regular(sync) function (componentDidMount). You can either do what I suggested, or use .then() to use setState after resolving and parsing the promise, in the actual componentDidMount function.
I suggest reading about async functions and Promise before continuing.
Best of luck!
Without testing your code, one problem is that you're incorrectly modifying state directly. That doesn't trigger a render and therefore your view is not updated. Try setState() instead, like so:
<li>{this.setState({modulesList[i]})}<li/>

Categories

Resources