使用模拟/存根在给定方法的摩卡中编写单元测试

时间:2019-08-13 14:13:08

标签: node.js request mocha sinon

我对node.js中的单元测试非常陌生。

我有一个看起来像这样的函数。我需要为该功能编写单元测试。

async refreshToken(req, res) {
    const token = req.body.token || req.query.token;
    const options = {method: 'POST', url: ''};
    let resp;
    try {
      resp = await request(options);
    } catch (e) {
      console.error(e);
    }
    if (resp) {
      const grant = {
        another_token: {
          token: resp.another_token,
        },
        expires_in: resp.expires_in
      }
      res.end(JSON.stringify(grant));
    } else {
      res.status(400).end("not authorized");
    }
  }

我计划将mocha框架与sinon和chai一起使用。

我尝试使用mocha进行测试,但无法弄清楚如何断言是否至少调用一次request(options)。

describe('refreshToken', () => {
    it('should take token from body', async () => {
      const req = {
        body: {
          token: "123"
        }
      }
      await auth.refreshToken(req, res);
      request.should.have.been.calledOnceWithExactly(options);
    })

我得到:

  

TypeError:无法读取未定义的属性“ xxxxx”

我在使用模拟/存根实现此功能时遇到困难。

1 个答案:

答案 0 :(得分:0)

这是单元测试解决方案:

auth.ts

import request from 'request-promise';

export async function refreshToken(req, res) {
  const token = req.body.token || req.query.token;
  const options = { method: 'POST', url: 'https://github.com/mrdulin' };
  let resp;
  try {
    resp = await request(options);
  } catch (e) {
    console.error(e);
  }
  if (resp) {
    const grant = {
      another_token: {
        token: resp.another_token
      },
      expires_in: resp.expires_in
    };
    res.end(JSON.stringify(grant));
  } else {
    res.status(400).end('not authorized');
  }
}

auth.spec.ts

import sinon from 'sinon';
import proxyquire from 'proxyquire';
import { expect } from 'chai';

describe('auth', () => {
  describe('#refreshToken', () => {
    it('should take token from body', async () => {
      const mResponse = { another_token: '123', expires_in: '3600' };
      const requestPromiseStub = sinon.stub().resolves(mResponse);
      const auth = proxyquire('./auth', {
        'request-promise': requestPromiseStub
      });
      const req = {
        body: {
          token: '123'
        }
      };
      const res = { end: sinon.stub() };
      await auth.refreshToken(req, res);
      expect(requestPromiseStub.calledWith({ method: 'POST', url: 'https://github.com/mrdulin' })).to.be.true;
      expect(
        res.end.calledWith(
          JSON.stringify({
            another_token: {
              token: mResponse.another_token
            },
            expires_in: mResponse.expires_in
          })
        )
      ).to.be.true;
    });

    it('should cause not authorized error', async () => {
      const mError = new Error('network error');
      const requestPromiseStub = sinon.stub().rejects(mError);
      const auth = proxyquire('./auth', {
        'request-promise': requestPromiseStub
      });
      const errorLogSpy = sinon.spy(console, 'error');
      const req = {
        body: {
          token: '123'
        }
      };
      const res = { status: sinon.stub().returnsThis(), end: sinon.stub() };
      await auth.refreshToken(req, res);
      expect(errorLogSpy.calledWith(mError)).to.be.true;
      expect(res.status.calledWith(400)).to.be.true;
      expect(res.status().end.calledWith('not authorized')).to.be.true;
    });
  });
});

带有覆盖率报告的单元测试结果:

  auth
    #refreshToken
      ✓ should take token from body (1262ms)
Error: network error
    at /Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/src/stackoverflow/57479631/auth.spec.ts:1:26462
    at step (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/src/stackoverflow/57479631/auth.spec.ts:1:23604)
    at Object.next (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/src/stackoverflow/57479631/auth.spec.ts:1:20644)
    at /Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/src/stackoverflow/57479631/auth.spec.ts:1:19788
    at new Promise (<anonymous>)
    at __awaiter (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/src/stackoverflow/57479631/auth.spec.ts:1:18995)
    at Context.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/src/stackoverflow/57479631/auth.spec.ts:1:26156)
    at callFn (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/node_modules/mocha/lib/runnable.js:387:21)
    at Test.Runnable.run (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/node_modules/mocha/lib/runnable.js:379:7)
    at Runner.runTest (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/node_modules/mocha/lib/runner.js:535:10)
    at /Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/node_modules/mocha/lib/runner.js:653:12
    at next (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/node_modules/mocha/lib/runner.js:447:14)
    at /Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/node_modules/mocha/lib/runner.js:457:7
    at next (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/node_modules/mocha/lib/runner.js:362:14)
    at Immediate.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/mocha-chai-sinon-codelab/node_modules/mocha/lib/runner.js:425:5)
    at runCallback (timers.js:705:18)
    at tryOnImmediate (timers.js:676:5)
    at processImmediate (timers.js:658:5)
      ✓ should cause not authorized error (88ms)


  2 passing (1s)

--------------|----------|----------|----------|----------|-------------------|
File          |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
--------------|----------|----------|----------|----------|-------------------|
All files     |      100 |       75 |      100 |      100 |                   |
 auth.spec.ts |      100 |      100 |      100 |      100 |                   |
 auth.ts      |      100 |       75 |      100 |      100 |                 4 |
--------------|----------|----------|----------|----------|-------------------|

源代码:https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/57479631