I've got a wrapper around the Axios XHR library, and I'm trying to write some unit tests for my code. However, when I run my tests, they all fail telling me that the timeout exceeded. How do I need to structure my tests in order to run my assertions?
Here's the wrapper code:
export const clientRequest = (xhrClient, endpoint, params = {}) => {
const method = params.method || 'get'
const {config, data, noOrg, unrestricted} = params
let reqParams = data
if (!isNil(data) && method === 'get') {
reqParams = {params: data}
}
const authConfig = unrestricted ? {...config, withCredentials: false} :
{...config, withCredentials: true}
const concatEndpoint = noOrg ? endpoint :
`${Cookies.get('organization') || 'default' }${endpoint}`
return new Promise((resolve, reject) => {
xhrClient[method](concatEndpoint, reqParams, authConfig)
.then(response => resolve(response))
.catch(err => reject(err))
})
}
And the test in question:
describe('clientRequest()', () => {
const resolveSpy = sinon.spy()
const fakeClient = {
get: () => new Promise(resolveSpy),
}
it.only('should make a call to the supplied method', (done) => {
const result = xhr.clientRequest(fakeClient, '/foobar', {method: 'get'})
result.then(() => {
expect(resolveSpy).to.have.beenCalledWith('/foobar', undefined, {withCredentials: true})
done()
})
})
})
It seems that fakeClient.get is never resolving.
Since it is the method that should be spied upon, I would suggest to change fakeClient this way:
const fakeClient = {
get: () => Promise.resolve()
}
sinon.spy(fakeClient, 'get');
It is also vital to add a catch call in order to handle errors in the promise chain:
result.then(() => {
expect(resolveSpy).to.have.beenCalledWith('/foobar', undefined, {withCredentials: true})
done()
}).catch(done);
I ended up following #MarcoL's suggestion and returned the expectation.
it('should make a call to the supplied method', () => {
const result = xhr.clientRequest(fakeClient, '/foobar', {method: 'get'})
return result.then(() => (
expect(resolveSpy).to.have.been.calledWith('/foobar', undefined, {withCredentials: true})
))
})
Related
im using an http request function as the handler function in middy and then use the ssm middleware to fetch some ssm parameters before initiating the http request.
like this:
const makeThirdPartyServiceRequest = middy(async ({ params }) => {
logger.info(`SENDING Request to ${endpoint} API`)
const url = `https://someurltoathirdpartyservice`
const options = {
method: 'POST',
body: params
}
return helpers.makeRequest(url, options)
})
makeThirdPartyServiceRequest.use(ssm(......))
However in my jest unit test Im trying to mock makeThirdPartyServiceRequest and explicitly say it should resolve to a value:
jest.mock('../src/thirdPartyService', () => ({
__esModule: true,
default: {
...(jest.requireActual('../src/thirdPartyService') as { default: {} }).default,
makeThirdPartyServiceRequest: jest.fn()
}
}))
export {}
import thirdPartyService from '../src/thirdPartyService'
And then in the test i say:
describe('makeThirdPartyServiceRequest()', () => {
it('should makeThirdPartyServiceRequest', async () => {
// Given
// })
const mockedThirdPartyServiceRequest = mocked(thirdPartyService.makeThirdPartyServiceRequest).mockResolvedValue({})
// When
const result = await thirdPartyService.makeThirdPartyServiceRequest(something)
// Then
expect(mockedThirdPartyServiceRequest).toHaveBeenCalledTimes(1)
expect(mockedThirdPartyServiceRequest.mock.calls[0][0].params.toString()).toBe(expectedParams)
})
})
However for some reason the middy middleware is still being invoked, which i clearly dont want and i have tried to mock away... what am i doing wrong?
You need to mock middy instead, to make it becomes a useless function. That function recipe a function as a parameter and return that parameter.
import thirdPartyService from '../src/thirdPartyService'
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockReturnValue(handler), // ...use(ssm()) will return handler function
}
}
})
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
hoangdv answer is also valid, but i will answer as well how i continued.
if you completely want to mock middy you mock like following:
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockImplementation(() => {
// ...use(ssm()) will return handler function
return {
before: jest.fn().mockReturnValue(handler)
}
})
}
}
})
However if you dont want to completely mock middy, you can instead mock the async getInternal function from middy/util called in before like this:
jest.doMock('#middy/util', () => ({
...(jest.requireActual('#middy/util') as {}),
getInternal: jest.fn()
}))
import { getInternal } from '#middy/util'
and then in the test
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(getInternal).mockResolvedValue({
twilioSecrets: { accountSid: 'someSID', serviceId:
'someServiceID', token: 'someToken' }
})
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
this will mock the async part of middy.
I have a list of proxies which I want to test if they are still active or not, after creating a promise list I run through the results, but after the last promise result the process doesnt stop and the programm freezes even though it should finish because there isnt any code after that.
The only fix I found was to put a process.exit() after running through all results, but this seems really odd to me. I want to automate the process but because the result function never ends its really difficult to workaround it.
const axios = require('axios')
const HttpsProxyAgent = require('https-proxy-agent');
const fs = require('fs');
const data = fs.readFileSync("./proxies.txt", "utf-8");
const proxyList = data.split("\n")
const axiosGet = (url, proxy) => {
const abort = axios.CancelToken.source()
const id = setTimeout(() => abort.cancel(`Timeout of 60000ms.`), 60000)
return axios.get(url, { cancelToken: abort.token, httpsAgent: new HttpsProxyAgent(`http://${proxy.trim()}`), proxy: false })
.then(response => { clearTimeout(id); return true })
.catch(error => { clearTimeout(id); return false })
}
const promises = proxyList.map((proxy, index) => {
return axiosGet(`https://api.ipify.org`, proxy, index)
}).map(p => p.catch(e => { e; return false }))
Promise.all(promises)
.then((results) => {
results.forEach((result, index) => console.log(index + 1, result))
//not exiting after running through all promises, only process.exit() works
})
.catch(err => console.log(err));
I have the following fetchData async function returning a message froma lambda function I want to take that response and dump it onto my page I am using the react-hooks-async package, with a useEffect inside of it. However when I start the function isnide the useAsyncTask it just contiunally runs and never gets the result. I could do it if I hooked up a button to the start() function and it would display correct, but I want it to run on load.
I am using Gatsby JS and react
var fetchData = async function run() {
const response = await fetch(fetchUrl, {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
customer_id: parsed.session_id,
}),
})
.then(res => {
return res.json()
})
.catch(error => console.log(error))
console.log(response)
return response
}
const Customer = () => {
const { start, started, result } = useAsyncTask(fetchData)
useEffect(() => {
console.log("result")
console.log(result)
console.log("result ends")
start()
}, [result])
return (
<div>
{started && "Fetching..."}
<div>Name: {result && result.message.customer_id}</div>
</div>
)
}
I was over engineering it. All that was required was the following
fetchData().then(value => console.log(value))
const Test = () => {
const [data, setData] = useState("")
useEffect(() => {
fetchData().then(test => {
setData(test)
})
}, [])
return data && <p>{data.message.customer_id}</p>
}
I have to do a functionality to test if 3 APIs are running.
Thus, the user will click on the Test APIs button and it will return the status of each API (status: 200, 500, 404 etc). If an API return an error, I should show the error stack.
Screen example:
API Status Detail
url1.com 200 -
url2.com 200 -
url3.com 500 internal server error
My question is, how can I call the 3 requests in parallel and return the async result, I mean how can I update the screen of API request status without having to wait for the result of all requests
I was basing on that How do I call three requests in order?, but it returns the result synchronously.
*******EDIT*****
Thats my current code
app.get('/testDependencies', function (req, res, next) {
let objTestsResul = {}
var urls = ['url1', 'url2', 'url3'];
let index = 0
while(urls.length > 0) {
let url = urls.shift();
objTestsResult[index++] = testURL(url)
}
res.send(objTestsResult)
});
This function is the same for each URL:
function testURL(URL){
fetch(URL, {
method: 'GET'
})
.then(res => {
res.json()
})
.then(json => {
console.log(json)
return json
})
.catch(error => {
return error
})
}
Promises (mdn) seem to be what you're looking for. They're essentially a more readable version of callbacks, which allow you to execute code when something else occurs rather than having to wait for that trigger to occur before resuming execution.
let endpoint1 = () => new Promise(resolve => setTimeout(() => resolve('200'), 1000));
let endpoint2 = () => new Promise(resolve => setTimeout(() => resolve('201'), 2000));
let endpoint3 = () => new Promise(resolve => setTimeout(() => resolve('500'), 1500));
document.getElementById('test').addEventListener('click', () => {
document.getElementById('status').textContent = 'test running...';
Promise.all([
endpoint1().then(a => document.getElementById('result1').textContent = a),
endpoint2().then(a => document.getElementById('result2').textContent = a),
endpoint3().then(a => document.getElementById('result3').textContent = a),
]).then(() => document.getElementById('status').textContent = 'test complete');
});
<button id="test">test</button>
<div>status: <span id="status">not running</span></div>
<div>endpoint 1: <span id="result1"></span></div>
<div>endpoint 2: <span id="result2"></span></div>
<div>endpoint 3: <span id="result3"></span></div>
This is actually pretty straightforward if you can use Bluebird:
const { Promise } = require('bluebird');
app.get('/testDependencies', function (req, res, next) {
Promise.map(['url1', 'url2', 'url3'], url => testURL(url)).then(results => {
res.send(results);
});
});
You'll just need to ensure your promise function actually returns a promise:
function testURL(URL) {
let start_time = new Date().getTime();
return fetch(URL, {
method: 'GET'
}).then(res => {
res.json()
}).then(json => {
console.log(json)
return json
}).catch(error => {
return error
})
}
Promises can't be dependency chained unless you explicitly return them from the function that's involved in chaining.
If you're able to use async and await, I'd also recommend doing that as well as that can vastly simplify otherwise complex code.
Express can't send multiple responses. You will have to finish all calls or use WebSockets to stream data.
function testURL(URL) {
return new Promise((resolve, reject) => {
if (URL === 'url2') {
reject(new Error('Internal Server Error'));
return;
}
resolve({ status: 200 });
});
}
const main = async () => {
const urls = ['url1', 'url2', 'url3'];
// return resolved and rejected Promises because if one fails in Promise.all
// the function will throw and we won't have any access to any resolved Promises.
const results = await Promise.all(urls
.map(url => testURL(url).then(response => response).catch(error => error)));
// every error have a stack property, Set the status to whatever you want
// based on the error and store the stack and the message
const objTestsResul = results.reduce((result, cur, i) => {
result[urls[i]] = cur.stack
? { status: 500, message: cur.message, stack: cur.stack }
: cur;
return result;
}, {});
console.log(objTestsResul);
};
main();
I've got a redux action creator that utilizes redux-thunk to do some logic to determine what to dispatch to the store. Its not promise-based, like an HTTP request would be, so I am having some issues with how to test it properly. Ill need a test for when the value meets the condition and for when it doesn't. Since the action creator does not return a promise, I cannot run a .then() in my test. What is the best way to test something like this?
Likewise, I believe it would be pretty straightforward testing the getRemoveFileMetrics() action creator as it actually does return a promise. But how can I assert that that will called if the value is removeFiles and meets the condition? How can that be written in the test?
Thanks in advance as this has had me stuck for the last couple of days.
Action Creators
export const handleSelection = (value, cacheKey) => {
return dispatch => {
if (value === "removeFiles") {
dispatch(getRemoveFileMetrics(cacheKey));
}
dispatch({ type: HANDLE_SELECTION, value });
};
};
export const getRemoveFileMetrics = cacheKey => {
return dispatch => {
dispatch({ type: IS_FETCHING_DELETE_METRICS });
return axios
.get(`../GetRemoveFileMetrics`, { params: { cacheKey } })
.then(response => {
dispatch({ type: GET_REMOVE_FILE_METRICS, payload: response.data });
})
.catch(err => console.log(err));
};
};
Jest
it("should dispatch HANDLE_SELECTION when selecting operation", () => {
const store = mockStore({});
const value = "switchVersion";
const expectedAction = [{
type: MOA.HANDLE_SELECTION,
value,
}]; // TypeError: Cannot read property 'then' of undefined
return store.dispatch(MOA.handleSelection(value)).then(() => {
const returnedActions = store.getActions();
expect(returnedActions).toEqual(expectedAction);
});
});
NEW EDIT
So based off of Danny Delott's answer to return a promise, I acheived a passing test as follows:
export const handleSelection = (value, cacheKey) => {
return dispatch => {
if (value === "removeFiles") {
return dispatch(getRemoveFileMetrics(cacheKey));
}
return new Promise((resolve, reject) => {
resolve(dispatch({ type: HANDLE_SELECTION, value }));
});
};
};
Is there a reason to explicitly NOT return a promise in your action creator? It looks like getRemoveFileMetrics is returning the promise, it just gets swallowed in handleSelection...
Easiest solution is to just return the promise:
export const handleSelection = (value, cacheKey) => {
return dispatch => {
if (value === "removeFiles") {
return dispatch(getRemoveFileMetrics(cacheKey));
}
dispatch({ type: HANDLE_SELECTION, value });
return new Promise();
};
};
Otherwise, you'll need make your assertions after the event loop is finished. You can do with a setTimeout wrapped in a Promise to get the .then behavior.
it("should dispatch HANDLE_SELECTION when selecting operation", () => {
const store = mockStore({});
const value = "switchVersion";
const expectedAction = [{
type: MOA.HANDLE_SELECTION,
value,
}];
store.dispatch(MOA.handleSelection(value));
// flush outstanding async tasks
return new Promise(resolve => {
setTimeout(resolve, 0);
})
.then(() => {
const returnedActions = store.getActions();
expect(returnedActions).toEqual(expectedAction);
});
});