Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test image upload (stream) with supertest and jest?

I have an image upload endpoint in my API that accepts application/octet-stream requests and handles these streams. I'd like to write test coverage for this endpoint but cannot figure out how to use supertest to stream an image.

Here's my code so far:

import request from 'supertest'

const testImage = `${__dirname}/../../../assets/test_image.jpg`

describe('Upload endpoint', () => {

  test('Successfully uploads jpg image', async () =>
    request(app)
      .post(`${ROOT_URL}${endpoints.add_image.route}`)
      .set('Authorization', `Bearer ${process.env.testUserJWT}`)
      .set('content-type', 'application/octet-stream')
      .pipe(fs.createReadStream(testImage))
      .on('finish', (something) => {
        console.log(something)
      }))

})

This code produces nothing, the finish event is never called, nothing is console logged, and this test suite actually passes as nothing is expected. I cannot chain a .expect onto this request, otherwise I get this runtime error:

TypeError: (0 , _supertest2.default)(...).post(...).set(...).set(...).pipe(...).expect is not a function

How is such a thing accomplished?

like image 203
j_d Avatar asked Mar 27 '18 10:03

j_d


People also ask

What is the difference between supertest and jest?

Jest - Jest is a JavaScript testing framework developed by Facebook. It works out of the box with minimal configuration and has in-built test runner, assertion library and mocking support. Supertest - A library for testing Node. js HTTP servers.


3 Answers

This should work. To pipe data to a request you have to tell the readable stream to pipe to the request. The other way is for receiving data from the server. This also uses done instead of async as pipes do not work with async/await.

Also worth nothing is that by default the pipe will call end and then superagent will call end, resulting in an error about end being called twice. To solve this you have to tell the pipe call not to do that and then call end in the on end event of the stream.

import request from 'supertest'

const testImage = `${__dirname}/../../../assets/test_image.jpg`

describe('Upload endpoint', () => {

  test('Successfully uploads jpg image', (done) => {
      const req = request(app)
          .post(`${ROOT_URL}${endpoints.add_image.route}`)
          .set('Authorization', `Bearer ${process.env.testUserJWT}`)
          .set('content-type', 'application/octet-stream')

      const imgStream = fs.createReadStream(testImage);
      imgStream.on('end', () => req.end(done));
      imgStream.pipe(req, {end: false})
  })
})

Edited to add: this has worked for me with small files. If I try testing it with a large test_image.jpg the request times out.

like image 194
popthestack Avatar answered Oct 20 '22 09:10

popthestack


I had to make assumptions about your upload method taking the body as input instead of multipart form-data. So below is an example where the raw body is passed for upload

const request = require('supertest');
const express = require('express');
const fs = require('fs')
const app = express();
var bodyParser = require('body-parser')
app.use(bodyParser.raw({type: 'application/octet-stream'}))

app.post('/user', function(req, res) {
    res.status(200).json({ name: 'tobi' });
});

testImage = './package.json'

resp = request(app)
    .post('/user')

    resp.set('Authorization', `Bearer Test`).set('Content-Type', 'application/octet-stream')

    resp.send(fs.readFileSync(testImage, 'utf-8'))
    resp.expect(200)
    .then(response => {
        console.log("response",response);
    }).catch((err) => {
        console.log(err)
    })

If you use multipart/form-data then below code shows an example

const request = require('supertest');
const express = require('express');
const fs = require('fs')
const app = express();

app.post('/user', function(req, res) {
    // capture the encoded form data
    req.on('data', (data) => {
        console.log(data.toString());
    });

    // send a response when finished reading
    // the encoded form data
    req.on('end', () => {
        res.status(200).json({ name: 'tobi' });
    });

});

testImage = './package.json'

resp = request(app)
    .post('/user')

    resp.set('Authorization', `Bearer Test`)
    resp.attach("file", testImage)
    resp.expect(200)
    .then(response => {
        console.log("response",response);
    }).catch((err) => {
        console.log(err)
    })
like image 27
Tarun Lalwani Avatar answered Oct 20 '22 08:10

Tarun Lalwani


const testImage = `${__dirname}/../../../assets/test_image.jpg`

describe('Upload endpoint', () => {

  test('Successfully uploads jpg image', async () =>
    request(app)
      .post(`${ROOT_URL}${endpoints.add_image.route}`)
      .set('Authorization', `Bearer ${process.env.testUserJWT}`)
      .attach("name",testImage,{ contentType: 'application/octet-stream' })
      .expect(200)
      .then(response => {
          console.log("response",response);
      })
  );
});
like image 1
Sajad Khoshnoudi Avatar answered Oct 20 '22 08:10

Sajad Khoshnoudi