【问题标题】:Can you make Supertest wait for an Express handler to finish executing?你能让 Supertest 等待 Express 处理程序完成执行吗?
【发布时间】:2018-12-11 21:39:33
【问题描述】:

我使用Supertest 来测试我的 Express 应用程序,但是当我希望我的处理程序在发送请求之后进行异步处理时,我遇到了挑战。以这段代码为例:

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

const app = express();

app.get('/user', async (req, res) => {
  res.status(200).json({ success: true });
  await someAsyncTaskThatHappensAfterTheResponse();
});

describe('A Simple Test', () => {
  it('should get a valid response', () => {
    return request(app)
      .get('/user')
      .expect(200)
      .then(response => {
          // Test stuff here.
      });
  });
});

如果someAsyncTaskThatHappensAfterTheResponse() 调用抛出一个错误,那么这里的测试会受到竞争条件的影响,它可能会或可能不会基于该错误而失败。即使除了错误处理之外,如果在设置响应后发生副作用,也很难检查它们。想象一下,您想在发送响应后触发数据库更新。您将无法从测试中判断出您应该期望更新何时完成。有什么方法可以使用 Supertest 等到处理函数执行完毕?

【问题讨论】:

  • 你找到解决这个问题的好方法了吗?

标签: node.js unit-testing express supertest


【解决方案1】:

受@travis-stevens 的启发,这里有一个稍微不同的解决方案,它使用了setInterval,因此您可以确保在进行超级测试之前设置了承诺。这也允许通过 id 跟踪请求,以防您想使用该库进行许多测试而不会发生冲突。

const backgroundResult = {};

export function backgroundListener(id, ms = 1000) {
  backgroundResult[id] = false;
  return new Promise(resolve => {
    // set up interval
    const interval = setInterval(isComplete, ms);
    // completion logic
    function isComplete() {
      if (false !== backgroundResult[id]) {
        resolve(backgroundResult[id]);
        delete backgroundResult[id];
        clearInterval(interval);
      }
    }
  });
}

export function backgroundComplete(id, result = true) {
  if (id in backgroundResult) {
    backgroundResult[id] = result;
  }
}

在您的supertest.request() 调用之前(在这种情况下,使用代理)进行调用以获取监听器承诺。

  it('should respond with a 200 but background error for failed async', async function() {
    const agent = supertest.agent(app);
    const trackingId = 'jds934894d34kdkd';
    const bgListener = background.backgroundListener(trackingId);

    // post something but include tracking id
    await agent
      .post('/v1/user')
      .field('testTrackingId', trackingId)
      .field('name', 'Bob Smith')
      .expect(200);

    // execute the promise which waits for the completion function to run
    const backgroundError = await bgListener;
    // should have received an error
    assert.equal(backgroundError instanceof Error, true);
  });

您的控制器应该期待跟踪 ID,并在控制器后台处理结束时将其传递给完整的函数。将错误作为第二个值传递是稍后检查结果的一种方法,但您可以传递 false 或任何您喜欢的值。

// if background task(s) were successful, promise in test will return true
backgroundComplete(testTrackingId);

// if not successful, promise in test will return this error object
backgroundComplete(testTrackingId, new Error('Failed'));

如果有人有任何 cmets 或改进,将不胜感激:)

【讨论】:

    【解决方案2】:

    这不容易做到,因为 supertest 就像一个客户端,而您无法访问 express 中的实际 req/res 对象(请参阅https://stackoverflow.com/a/26811414/387094)。

    作为一个完整的 hacky 解决方法,这对我有用。

    创建一个包含回调/承诺的文件。例如,我的文件 test-hack.js 如下所示:

    let callback = null
    export const callbackPromise = () => new Promise((resolve) => {
      callback = resolve
    })
    export default function callWhenComplete () {
      if (callback) callback('hack complete')
    }
    

    当所有处理完成后,调用回调callWhenComplete函数。例如,我的中间件是这样的。

    import callWhenComplete from './test-hack'
    
    export default function middlewareIpnMyo () {
      return async function route (req, res, next) {
        res.status(200)
        res.send()
    
        // async logic logic
        callWhenComplete()
      }
    }
    

    最后在你的测试中,像这样等待 callbackPromise:

    import { callbackPromise } from 'test-hack'
    
      describe('POST /someHack', () => {
        it.only('should handle a post request', async () => {
    
          const response = await request
            .post('/someHack')
            .send({soMuch: 'hackery'})
            .expect(200)
    
          const result = await callbackPromise()
    
          // anything below this is executed after callWhenComplete() is 
          // executed from the route
    
        })
    })
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多