【问题标题】:Javascript issue when using let使用 let 时的 Javascript 问题
【发布时间】:2019-03-04 01:50:55
【问题描述】:

我有以下 js 用于对错误处理程序进行单元测试:

import assert from 'assert';
import deepClone from 'lodash.clonedeep';
import deepEqual from 'lodash.isequal';
import { spy } from 'sinon';
import errorHandler from './index';

function getValidError(constructor = SyntaxError) {
  let error = new constructor();
  error.status = 400;
  error.body = {};
  error.type = 'entity.parse.failed';
  return error;
}

describe('errorHandler', function() {
  let err;
  let req;
  let res;
  let next;
  let clonedRes;
  describe('When the error is not an instance of SyntaxError', function() {
    err = getValidError(Error);
    req = {};
    res = {};
    next = spy();
    clonedRes = deepClone(res);
    errorHandler(err, req, res, next);

    it('should not modify res', function() {
      assert(deepEqual(res, clonedRes));
    });

    it('should call next()', function() {
      assert(next.calledOnce);
    });
  });

  ...(#other test cases all similar to the first)

  describe('When the error is a SyntaxError, with a 400 status, has a `body` property set, and has type `entity.parse.failed`', function() {
    err = getValidError();
    req = {};
    let res = {
      status: spy(),
      set: spy(),
      json: spy()
    };
    let next = spy();
    errorHandler(err, req, res, next);

    it('should set res with a 400 status code', function() {
      assert(res.status.calledOnce);
      assert(res.status.calledWithExactly(400));
    });

    it('should set res with an application/json content-type header', function() {
      assert(res.set.calledOnce);
      assert(res.set.calledWithExactly('Content-Type', 'application/json'));
    });

    it('should set res.json with error code', function() {
      assert(res.json.calledOnce);
      assert(res.json.calledWithExactly({ message: 'Payload should be in JSON format' }));
    });
  });
});

请注意,在“当错误是 SyntaxError...”的描述块中,resnextclonedRes 前面有 let

如果没有let 在这些前面,我的测试会失败。我不明白为什么我需要再次为这些添加let,而不是在同一块中添加errreq。谁能帮我解释一下?

【问题讨论】:

  • 显式声明变量总是一个好习惯(使用varlet)。 Javascript 应该理解隐式声明的变量,但它可能会遇到问题,就像你的情况一样。

标签: javascript unit-testing let


【解决方案1】:

在严格模式下(以及通常在经过适当 linted 的代码中),变量必须在分配之前声明。此外,constlet 变量必须在块中声明一次,不能再声明更多。重新声明已声明的 err(或任何其他变量)将引发错误,这就是为什么您应该在 describe('errorHandler' 函数中只看到一次 let <varname>

const describe = cb => cb();

let something;
describe(() => {
  something = 'foo';
});
let something;
describe(() => {
  something = 'bar';
});

进一步的describes inside of describe('errorHandler' 已经拥有对err 的范围访问权限。

根本不首先声明变量,在草率模式下分配给它会导致它被分配给全局对象,这几乎总是不受欢迎的can introduce bugs and errors。例如:

// Accidentally implicitly referencing window.status, which can only be a string:

status = false;
if (status) {
  console.log('status is actually truthy!');
}

也就是说,保持变量范围尽可能窄通常是个好主意 - 仅当您需要外部范围内的值时才将其分配给外部变量。考虑只在分配给它们的describes 内声明变量,这还有一个额外的好处是允许您使用const 而不是let

describe('When the error is not an instance of SyntaxError', function() {
  const err = getValidError(Error);
  const req = {};
  const res = {};
  const next = spy();
  const clonedRes = deepClone(res);
  errorHandler(err, req, res, next);
  // etc
});
// etc
describe('When the error is a SyntaxError, with a 400 status, has a `body` property set, and has type `entity.parse.failed`', function() {
  const err = getValidError();
  const req = {};
  const res = {
    status: spy(),
    set: spy(),
    json: spy()
  };
  const next = spy();
  // etc

【讨论】:

  • 感谢您的回答,它很有用。我基本上是想避免在任何地方都输入const。为什么使用const 而不是let 有好处?
  • 当您知道变量不会被重新分配时,它会使代码更具可读性。例如,考虑const foo = 'foo'; <30 lines of code> <do something with foo>,您知道foo 肯定是'foo',而无需仔细查看这30 行中的任何foo = <somethingElse>。应尽可能避免重新分配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-20
  • 1970-01-01
  • 2022-12-01
  • 2020-06-02
  • 2023-03-08
  • 2011-09-15
  • 1970-01-01
相关资源
最近更新 更多