【问题标题】:How to test image upload (stream) with supertest and jest?如何用 supertest 和 jest 测试图像上传(流)?
【发布时间】:2018-04-07 18:06:49
【问题描述】:

我的 API 中有一个图像上传端点,它接受 application/octet-stream 请求并处理这些流。我想为此端点编写测试覆盖率,但不知道如何使用 supertest 流式传输图像。

到目前为止,这是我的代码:

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

})

这段代码没有产生任何结果,finish 事件从未被调用,控制台没有记录任何内容,而且这个测试套件实际上没有任何预期通过。我无法将.expect 链接到此请求,否则我会收到此运行时错误:

TypeError: (0 , _supertest2.default)(...).post(...).set(...).set(...).pipe(...).expect 不是功能

这样的事情是怎么做到的?

【问题讨论】:

  • 要将数据传输到请求,您必须告诉可读流传输到请求。例如imageStream.pipe(req).
  • async/await 仅适用于承诺,不适用于流。您需要更改测试函数以使用(done) 并在管道的finish/close 事件中调用done()
  • @popthestack 如果你写了一个完整的工作答案,我很乐意给你赏金。
  • @IsaacHinman,这里的请求对象是什么?如果可能的话,提供一个最小的 git repo,你应该尽快得到修复
  • @TarunLalwani 抱歉,request 是问题标题中所示的 supertest 库。

标签: javascript streaming jestjs supertest superagent


【解决方案1】:

这应该可以。要将数据传输到请求,您必须告诉可读流传输到请求。另一种方式是从服务器接收数据。这也使用 done 而不是 async,因为管道不适用于 async/await。

同样没有价值的是,默认情况下管道会调用end,然后superagent会调用end,导致end被调用两次的错误。要解决这个问题,您必须告诉管道调用不要这样做,然后在流的 on end 事件中调用 end。

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

编辑添加:这对我来说适用于小文件。如果我尝试使用较大的 test_image.jpg 对其进行测试,则请求会超时。

【讨论】:

    【解决方案2】:
    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);
          })
      );
    });
    

    【讨论】:

    • 我认为您不应该同时使用expectthen。无论如何,在运行您的代码时(这与我已经尝试过的事情非常相似),我收到标题内容类型为 not application/octet-stream 的错误。 attach 方法似乎重写了标题。
    • 是的,我忘了删除内容类型。我更新了答案
    • @IsaacHinman,我认为这应该可以,你能检查并更新吗?
    【解决方案3】:

    我不得不假设您的上传方法将正文作为输入而不是多部分表单数据。所以下面是一个传递原始正文进行上传的示例

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

    如果您使用multipart/form-data,则下面的代码显示了一个示例

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

    【讨论】:

      【解决方案4】:

      我认为您实际上想使用fs.createReadStream(testImage) 将该图像读入您的请求,因为fs.createWriteStream(testImage) 会将数据写入提供的文件描述符(在本例中为testImage)。 Feel free to checkout Node Streams to see how they work in more detail.

      我不太确定您从哪里获得 supertestfinish 事件,但您可以了解如何使用 .pipe() 方法 here

      如果更适合您的测试,您可能还想考虑使用 supertest multipart attachments

      【讨论】:

      • 对不起,这只是我的一个错字。当然,我们正在读入流。
      猜你喜欢
      • 2019-07-31
      • 2021-08-15
      • 2019-02-11
      • 2021-10-21
      • 2018-06-22
      • 2020-09-14
      • 1970-01-01
      • 2021-11-28
      • 1970-01-01
      相关资源
      最近更新 更多