Async Testing axios in node, and getting a timeout error - javascript

I have my Axios code, which should download a file (cribbed and changed from here).
'use strict'
const Fs = require('fs')
const Path = require('path')
const axios = require('axios')
const URL = 'https://unsplash.com/photos/AaEQmoufHLk/download?force=true'
async function downloadMtgJson() {
const path = Path.resolve(__dirname, 'resources', 'code.jpg')
const writer = Fs.createWriteStream(path)
const response = await axios({
URL,
method: 'GET',
responseType: 'stream'
})
console.log(response.data.pipe)
if (response && response.data) response.data.pipe(writer);
console.log('hello')
return handleWriter(writer)
}
const handleWriter = (writer) => new Promise((resolve, reject) => {
writer.on('finish', resolve)
writer.on('error', reject)
})
module.exports = {
downloadMtgJson,
handleWriter,
URL
}
And I looked for an example of axios tests for this online, and some SO questions, and found some code I've also tweaked to my purposes:
const axios = require('axios');
const { downloadMtgJson, URL } = require('./resources');
jest.mock('axios');
describe.only('fetchData', () => {
it('fetches successfully data from an URL', async () => {
expect.assertions(1);
const data = { status: 200, data: { pipe: () => 'data' } };
axios.mockImplementationOnce(() => Promise.resolve(data));
console.log('waiting...')
await expect(downloadMtgJson()).resolves.toEqual('data');
console.log('waited')
expect(axios).toHaveBeenCalledWith(
`${URL}/search?query=react`,
);
});
});
The issue I am having, is that the promise part of the handleWriter seems to be timing out. At one point I mocked it in the same way as I mocked axios, but it didn't make a difference.
Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:
6 |
7 | describe.only('fetchData', () => {
> 8 | it('fetches successfully data from an URL', async () => {
I can't figure out what is different in what I've done, vs what I've seen in testing examples.
What can I do to get my tests working?

This line is also asynchronous:
const writer = Fs.createWriteStream(path)
And needs mocking out to prevent the error you see.
The full test code is now:
jest.mock('fs');
jest.mock('axios');
jest.mock('./resource.util')
// ... other code ...
it('fetches successfully data from an URL', async () => {
const data = { status: 200, data: { pipe: () => 'data' } };
axios.mockImplementationOnce(() => data);
fs.createWriteStream.mockImplementationOnce(() => 'fs');
handleWriter.mockImplementationOnce(() => 'data'); //handle writer was extracted to a util file
const expectedJson = await downloadMtgJson();
expect(expectedJson).toEqual('data');
expect(axios).toHaveBeenCalledWith(
expect.objectContaining({ URL: 'https://unsplash.com/photos/AaEQmoufHLk/download?force=true' }),
);
expect(axios).toHaveBeenCalledWith(
expect.objectContaining({ responseType: 'stream' }),
);
});

Related

How to test in JEST a request that downloads a file

I want to unit test the exported method in the code below. Trying to write unit tests for a function that is downloading a zip file from a localhost server.I will write my function bellow so you understand better:
export const downloadCdn = async (cdnUrl, out) => {
const download = (resolve, reject) => {
const req = request({
method: 'GET',
uri: cdnUrl
});
req.on('response', (data) => {
// do something
});
req.on('error', (data) => {
// do something
});
req.on('data', (chunk) => {
// do something
});
req.on('end', () => {
console.log('download done');
});
req.pipe(out);
out.on('close', () => {
resolve([null, 'done']);
});
};
const downloadSummary = new Promise(download);
return downloadSummary
.then(() => [null, 'Done'])
.catch(err => [err, null]);
};
Here are my test file, what I'm trying to achieve is to have unit test that validates the download of the zip file:
import request from 'request';
import * as Module from './downloadCdn';
jest.mock('request', () => {
const mockRequest = {
pipe: jest.fn(),
on: jest.fn(),
};
return function () {
return mockRequest;
};
});
describe('Downloading a file', () => {
it('Should find the module', () => {
expect(typeof Module.downloadCdn === 'function').toBeTruthy();
});
it('Should download the zip', async () => {
const [error, response] = await Module.downloadCdn(cdnUrl, out);
expect(response === 'Done').toBeTruthy();
expect(error === null).toBeTruthy();
});
});
The response from the Promise, I receive inside the test is null, no error catching. Here is the error received from jest:
expect(received).toBeTruthy()
Expected value to be truthy, instead received false
While mocking the request, you should resolve the promise. I think that promise is not resolving that's why it's not working. I hope that the below code will be fixed your problem.
jest.mock('request', () => {
const mockRequest = {
pipe: jest.fn(),
on: (parameter, callback) => {
callback();
},
};
return function () {
return mockRequest;
};
});

Jest Axios test is passing when it shouldn't

I am writing a script (see below) to make sure that an axios function throws an error when it receives a certain status code. I found out, however, that even when I make this test fail, Jest still says that the test passes, even though it returns an error in the console (see below). Why is Jest saying this test has passed when it actually failed? Does it have something to do with me trying to expect an error, so even if the test fails, jest still receives an error (that the test failed) and thinks this means that I got what I expected? Thanks.
foo.test.js:
import axios from 'axios';
jest.mock('axios', () => ({
get: jest.fn(() => Promise.resolve({ data: 'payload' })),
}));
const getData = async (url) => {
const response = await axios.get(url);
if (response.status !== 200) {
return response.text().then((error) => {
throw new Error(error.message);
});
} else {
return response.data;
}
};
test('testing that an error is thrown', async () => {
axios.get.mockImplementation(() =>
Promise.resolve({
data: {data: 'payload'},
status: 400,
text: () => Promise.resolve(JSON.stringify({message: 'This is an error.'})),
})
);
const expectedError = async () => {
await getData('sampleUrl');
};
// The error should return 'This is an error.' and instead
// is expecting 'foo', so this test should fail.
expect(expectedError()).rejects.toThrowError('foo');
});
You need two changes to get the test to fail as expected.
Don't stringify the resolved value from text
await the expect that uses rejects
Here is an updated version that fails as expected:
import axios from 'axios';
jest.mock('axios', () => ({
get: jest.fn(() => Promise.resolve({ data: 'payload' })),
}));
const getData = async (url) => {
const response = await axios.get(url);
if (response.status !== 200) {
return response.text().then((error) => {
throw new Error(error.message);
});
} else {
return response.data;
}
};
test('testing that an error is thrown', async () => {
axios.get.mockImplementation(() =>
Promise.resolve({
data: {data: 'payload'},
status: 400,
text: () => Promise.resolve({message: 'This is an error.'}), // <= don't stringify
})
);
const expectedError = async () => {
await getData('sampleUrl');
};
await expect(expectedError()).rejects.toThrowError('foo'); // <= await
});

Node.js - Mock result of a promise

I want to mock the result of a function within a node module so that i can run assertions.
Considering the following node module:
const doPostRequest = require('./doPostRequest.js').doPostRequest;
const normalizeSucessResult = require('./normalizer.js').normalizeSucessResult;
const normalizeErrorResult = require('./normalizer.js').normalizeErrorResult;
exports.doPost = (params, postData) => {
return doPostRequest(params, postData).then((res) => {
const normalizedSuccessResult = normalizeSucessResult(res);
return normalizedSuccessResult;
}).catch((err) => {
const normalizedErrorResult = normalizeErrorResult(err);
return normalizedErrorResult;
})
}
The function doPostRequest returns a promise. How can i fake the return value of this promise so that i can assert if normalizeSucessResult has been called?
So for i have tried:
const normalizeSucessResult = require('./normalizer.js');
const doPostRequest = require('./doPostRequests.js');
const doPost = require('./doPost.js');
it('runs a happy flow scenario', async () => {
let normalizeSucessResultStub = sinon.stub(normalizeSucessResult, 'normalizeSucessResult');
let postData = { body: 'Lorum ipsum' };
let params = { host: 'someUrl', port: 433, method: 'POST', path: '/' };
sinon.stub(doPostRequest, 'doPostRequest').resolves("some response data"); //Fake response from doPostRequest
return doPost.doPost(params, postData).then((res) => { //res should be equal to some response data
expect(normalizeSucessResultStub).to.have.been.calledOnce;
expect(normalizeSucessResultStub).to.have.been.with("some response data");
});
});
The doPostRequest module looks like this:
const https = require('https')
module.exports.doPostRequest = function (params, postData) {
return new Promise((resolve, reject) => {
const req = https.request(params, (res) => {
let body = []
res.on('data', (chunk) => {
body.push(chunk)
})
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString())
} catch (e) {
reject(e)
}
resolve(body)
})
})
req.on('error', (err) => {
reject(err)
})
if (postData) {
req.write(JSON.stringify(postData))
}
req.end()
})
}
You can use Promise.resolve to return a promise with any given value.
Promise.resolve(“hello world”);
For stub your func you need to do like this
sinon.stub({doPostRequest}, 'doPostRequest').resolves("some response data")
Okay, i figured it out. The function doPostRequest was loaded using require, on the top of the file using const doPostRequest = require('./doPostRequest.js').doPostRequest;
In order to mock the data that comes back from a function that is loaded using require i had to use a node module called mock-require. There are more modules that can take care of this (proxyquire is a populair one) but i picked mock-require (i did not have a specific reason for choosing mock-require).
For anyone else that is stuck with a similar problem, try mock-require to mock the respose from files that are loaded using require.

Node-scheduler error after implement test

After I have successfully implemented one of methods to fetch some data and run test
Code:
const fetchData = async (url) => {
const response = await axios.get(url);
const contentType = response.headers['content-type'];
if (typeof response.data === 'object') {
return JSON.stringify(response.data);
}
throw new Error('Content-Type is unrecognized');
};
module.exports = fetchData;
And test:
describe('fetchData', () => {
it('should return json string response data on successful request', async () => {
const responseData = await fetchData(url);
const expectedData = JSON.stringify({ key1: 'value1' });
assert.deepEqual(responseData, expectedData, 'Response data doesn\'t match');
});
However, I wanted to implement scheduling to my method. I implemented in by using node-scheduler npm module.
After my modification
scheduler.scheduleJob({ start: startTime, end: endtTime }, async () => {
const fetchData = async (url) => {
const response = await axios.get(url);
}
Tests are failing immadietly, furthermore I noticed that error log is going continuously, therefore I have to kill test.
Does anyone have an idea why adding simple scheduler makes my error not working? I am using:
Node v.8.11.4
chai-as-promised
nock

how can i download a video mp4 file using node.js?

I want to let users download a video from my AWS S3 bucket. The video format is MP4:
app.get("/download_video", function(req,res) {
filename = "s3.xxx.amazon.com/bucketname/folder/video_example.mp4";
// im stuck on what i can do here
});
There are a lot of examples on how to download images and textfiles online using nodejs, but I can't find anything on videos.
use strict
const Fs = require('fs')
const Path = require('path')
const Listr = require('listr')
const Axios = require('axios')
function one (tasks) {
tasks.run()
.then(process.exit)
.catch(process.exit)
}
if (process.argv) {
const tasks = [{
title: 'Downloading',
task: async (ctx, task) => {
const url = 'https://s3.xxx.amazon.com/bucketname/folder/video_example.mp4"'
const path = Path.resolve(__dirname, 'media', 'video.mp4')
const response = await Axios({
method: 'GET',
url: url,
responseType: 'stream'
})
response.data.pipe(Fs.createWriteStream(path))
return new Promise((resolve, reject) => {
response.data.on('end', () => {
resolve()
})
response.data.on('error', err => {
reject(err)
})
})
}
}]
one(new Listr(tasks))
}
Try this
const fetch = require('node-fetch');
const fs = require('fs');
const response = await fetch(yourUrl);
const buffer = await response.buffer();
fs.writeFile(`./videos/name.mp4`, buffer, () =>
console.log('finished downloading video!'));
Third-party modules are no longer needed as of Node.js v18.
import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
const videoFileUrl = 'https://sveltejs.github.io/assets/caminandes-llamigos.mp4';
const videoFileName = 'video.mp4';
if (typeof (fetch) === 'undefined') throw new Error('Fetch API is not supported.');
const response = await fetch(videoFileUrl);
if (!response.ok) throw new Error('Response is not ok.');
const writeStream = createWriteStream(videoFileName);
// Reference https://stackoverflow.com/a/66629140/12817553
const readable = Readable.fromWeb(response.body);
readable.pipe(writeStream);
await new Promise((resolve, reject) => {
readable.on('end', resolve);
readable.on('error', reject);
});

Categories