最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to test image upload (stream) with supertest and jest? - Stack Overflow

programmeradmin1浏览0评论

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?

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?

Share Improve this question edited Apr 6, 2018 at 11:05 j_d asked Mar 27, 2018 at 10:20 j_dj_d 3,08210 gold badges55 silver badges96 bronze badges 7
  • To pipe data to a request you have to tell the readable stream to pipe to the request. e.g. imageStream.pipe(req). – popthestack Commented Apr 5, 2018 at 16:46
  • async/await only works with promises, not streams. You'll need to change the test function to use (done) and call done() in the pipe's finish/close event. – popthestack Commented Apr 5, 2018 at 17:07
  • @popthestack If you write up a full working answer I'd be happy to give you the bounty. – j_d Commented Apr 6, 2018 at 8:34
  • @IsaacHinman, what is the request object here? If possible provide a minimal git repo and you should get the fix soon – Tarun Lalwani Commented Apr 6, 2018 at 9:48
  • @TarunLalwani Sorry for that, request is the supertest library as indicated in the question title. – j_d Commented Apr 6, 2018 at 11:06
 |  Show 2 more comments

4 Answers 4

Reset to default 7

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.

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);
      })
  );
});

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)
    })

I think you actually want to use fs.createReadStream(testImage) to read that image into your request, since fs.createWriteStream(testImage) would be writing data into the file descriptor provided (in this case testImage). Feel free to checkout Node Streams to see how they work in more detail.

I'm not quite sure where you're getting the finish event from for supertest, but you can see how to use the .pipe() method here.

You might also want to consider using supertest multipart attachments, if that better suits your test.

发布评论

评论列表(0)

  1. 暂无评论