首页 > 解决方案 > 在 Express 路由的 Mocha 单元测试中使用 Sinon 存根环境变量

问题描述

我正在努力做到这一点,所以在我的 TypeScript API 中调用测试路由时,任何需要环境变量的代码都被抽象掉了,所以我们没有连接到任何真实的服务器等。

我最初的快递代码是

router.post("/hello", async (
  req: express.Request,
  res: express.Response,
  next: express.NextFunction
) => {
  const value = await getAValue(req.body)
  ...
}

getAValue()函数是使用环境变量的函数,所以我正在使用 Supertest 为这条路线编写一个测试,如下所示

  import request from "supertest";
  import App, { getAValue } from "./src";

  describe("POST /hello", () => {
    it("Returns 200 on expected input", (done) => {
      const payload = {
        "foo":"bar"
      }

      request(App)
        .post("/api/hello")
        .send(payload)
        .expect(200)
        .end((err) => {
          if (err) {
            done(err)
          } else {
            done();
          }
        });
     });
  });

我正在使用存根文件存根任何必需的函数test/stubs.ts

import sinon from "sinon"
import { getAValue } from "../src"

sinon.stub(getAValue);

test并在我的脚本中使用我的脚本package.json来查找这些存根

"test": "mocha -r ts-node/register -r test/stubs.ts --config=test/.mocharc.json 'test/**/*.ts' --exit"

所以在getAValue()函数中有一个

import * as env from "env-var";

const value = env.get("VAR_NAME").required().asString();

但我不知何故无法嘲笑与此相关的任何事情并且总是得到同样的错误 ERROR: EnvVarError: env-var: "VAR_NAME" is a required variable, but it was not set

我试过模拟那个函数,或者特别是对process.env但都不起作用的调用。

为 Express 路由测试模拟环境变量的正确方法是什么?

标签: node.jsexpressunit-testingmocha.jssinon

解决方案


在您的情况下,我有 2 个选择:a)存根 getAValue() 和b)存根 env.get()。您可以根据自己的情况选择它。

对于这个完整的示例,我使用您的代码并对其进行了一些修改:

// 1. File: src/index.ts
import express from 'express';
import * as util from './util';

const app = express();

app.use(express.json());

app.post('/hello', async (
  req: express.Request,
  res: express.Response,
  next: express.NextFunction
) => {
  // Need to know whether this function get called.
  console.log('/hello called');
  // Note: Do not use destructure object to call getAValue().
  const value = util.getAValue(req.body);
  // Check for return value.
  console.log('getAValue return:', value);
  // Simplify the request end.
  res.end();
});

export default app;
// 2. File: src/util.ts
import env from 'env-var';

const getAValue = (body: any) => {
  // Need to know whether this function get called.
  console.log('Real getAValue called');
  // This is based on your code.
  const value = env.get('VAR_NAME').required().asString();
  return value;
};

export { getAValue };
// 3. File: test/index.spec.ts
import request from 'supertest';
import app from '../src';

describe('POST /hello', () => {
  it('Returns 200 on expected input', (done) => {
    const payload = {
      'foo':'bar'
    }

    request(app)
      .post('/hello')
      .send(payload)
      .expect(200)
      .end((err) => {
        if (err) {
          done(err)
        } else {
          done();
        }
      });
   });
});

现在是重要文件:test/stubs.ts

替代 a)存根 getAValue()

// 4a File: test/stubs.ts
import sinon from 'sinon';
import * as util from '../src/util';

sinon.stub(util, 'getAValue').callsFake((input: any) => {
  console.log('fake getAValue called');
  // Input here is: req.body or payload from test.
  return input;
});

我从终端运行它时的结果:

$ npm run test

> 69006601@1.0.0 test
> mocha -r ts-node/register -r test/stubs.ts 'test/**/*.ts' --exit



  POST /hello
/hello called
fake getAValue called
getAValue return: { foo: 'bar' }
    ✔ Returns 200 on expected input


  1 passing (22ms)

在这个替代方案中,真正的 getAValue 函数永远不会被调用,而假的 getAValue 函数会被调用。您可以在假函数中设置测试环境。

替代 b)存根 env.get()

// 4b File: test/stubs.ts
import sinon from 'sinon';
import env from 'env-var';

sinon.stub(env, 'get').callsFake(function (input: any) {
  // Input is VAR_NAME.
  console.log('fake env.get called: ', input);
  // Supply with test env.
  return env.from({ VAR_NAME: 'test' }).get(input);
});

我从终端运行它时的结果:

$ npm run test

> 69006601@1.0.0 test
> mocha -r ts-node/register -r test/stubs.ts 'test/**/*.ts' --exit



  POST /hello
/hello called
Real getAValue called
fake env.get called:  VAR_NAME
getAValue return: test
    ✔ Returns 200 on expected input


  1 passing (22ms)

在这个替代方案中,真正的 getAValue 会被调用,但真正的 env.get() 不会被调用,因此您可以设置 env 进行测试。


推荐阅读