我正在Node项目中创建一个基础类,我想使用Jest对其进行测试。我收到一个错误,暗示要在测试中使用“严格”模式,我想避免/修复该错误
root
我正在对此进行测试:
module.exports = class LogRecord {
constructor(level, message, timestamp) {
this.level = level;
this.message = message;
this.timestamp = timestamp ? timestamp : Date.now();
}
get level() {
return this.level;
}
get message() {
return this.message;
}
get timestamp() {
return this.timestamp;
}
}
运行let LogRecord = require('../lib/logRecord');
describe('Test LogRecord functionality', () => {
test('LogRecord constructor', () => {
let timestamp = Date.now();
let logRecord = new LogRecord('INFO', 'Test Message', timestamp);
expect(logRecord.level).toBe('INFO');
expect(logRecord.message).toBe('Test Message');
expect(logRecord.timestamp).toBe(timestamp);
});
test('LogRecord is read-only', () => {
let timestamp = Date.now();
let logRecord = new LogRecord('INFO', 'Test Message', timestamp);
logRecord.level = 'WARN'
logRecord.message = 'New Message'
logRecord.timestamp = Date.now();
expect(logRecord.level).toBe('INFO');
expect(logRecord.message).toBe('Test Message');
expect(logRecord.timestamp).toBe(timestamp);
});
});
时,两个LogRecord测试都出现以下错误:
npm test
Test LogRecord functionality › LogRecord constructor
TypeError: Cannot set property level of #<LogRecord> which has only a getter
1 | module.exports = class LogRecord {
2 | constructor(level, message, timestamp) {
> 3 | this.level = level;
| ^
4 | this.message = message;
5 | this.timestamp = timestamp ? timestamp : Date.now();
6 | }
at new LogRecord (lib/logRecord.js:3:9)
at Object.test (test/logRecord.test.js:6:25)
答案 0 :(得分:3)
测试是要确保您的代码执行了您认为的工作。考虑以下课程:
class Foo {
constructor (bar) {
this._bar = bar;
}
get bar () {
return this._bar;
}
}
这里bar
是只读的,无法设置bar
属性:
let foo = new Foo('a foo');
foo.bar; // 'a foo'
foo.bar = 'am not'; // TypeError!
模块问题并非真正相关:因为注释类主体中链接的Logar始终是 严格模式,
因此,如果您希望属性是只读的,则无需担心编写它。工作流程可能看起来像这样:
class Foo {}
并构造一个实例foo = new Foo()
如果您不希望使用只读属性,则可以添加一个setter:
class Foo {
constructor (bar) {
this._bar = bar;
}
get bar () {
return this._bar;
}
set bar (value) {
this._bar = value;
}
}
在这种情况下,您将添加一个设置bar
的测试,并读取更改后的值。
*您可能想知道为什么当规范保证了这种行为时为什么要进行此测试,并且我认为该测试是必需的,因为有人(对于调用者而言是透明的)可以将该类重构为老式构造函数,并且为错误创建向量:
// post refactor Foo
const Foo = function Foo(bar) {
this.bar = bar; // danger! now writable!
};
希望这类事情会被知识渊博的审阅者发现,但是我还是会编写测试。
如果您想要的是在构造函数中设置的保证的只读属性,则可以使用以下方法:
const data = new WeakMap();
module.exports = class Foo () {
constructor (bar) {
data.set(this, bar);
}
get bar () {
return data.get(this);
}
};
因为没有导出data
,所以没有外部代码可以更改它。尝试设置实例的bar
属性将引发错误。只是使用getter和setter定义下划线属性,这有点复杂,但是如果您要使用它,那么……我知道这种模式,因为我已经使用了它。
您只能为每个模块创建一个弱映射,而不是为每个类或实例创建一个。弱映射存储一个唯一数据条目,该条目键入到各个实例(即this
):
const data = new WeakMap();
module.exports = {
Foo: class Foo () {
constructor (bar) {
data.set(this, bar);
}
get bar () {
return data.get(this);
}
},
Bar: class Bar () {
constructor (prop1, prop2) {
// for multiple props we'll store an object...
data.set(this, { prop2, prop1 });
}
get prop1 () {
// ...and retrieve it and access it's props to return
return data.get(this).prop1;
}
get prop2 () {
return data.get(this).prop2;
}
}
};
请注意,使用吸气剂设置道具,但不会有任何设置器会抛出...
// in someotherfile.js
const { Foo } = require('path/to/file/with/foo.js');
const foo = new Foo('imma foo');
foo.bar; // 'imma foo'
foo.bar = 'no not really'; // TypeError!
// you can still set random properties that don't have a getter:
foo.baz = 'I do not throw';
foo.baz; // 'I do not throw'
答案 1 :(得分:2)
如果希望仅在对象初始化之后读取属性,则可以在构造函数中使用Object.freeze,并删除您的吸气剂:
class LogRecord {
constructor(level, message, timestamp) {
this.level = level;
this.message = message;
this.timestamp = timestamp ? timestamp : Date.now();
Object.freeze(this);
}
}
但这会冻结对象的所有属性。之后,您将无法修改,删除或添加任何内容。没有深入研究这一点,因此它可能也存在一些缺陷