使用 class 属性的 toEqual 变体与使用 class 方法的方式相同

Variant of toEqual that works with class properties in the same way that it works with class methods

场景是这样的。第一个 class 有一个方法 getName,第二个 class 有一个 class 属性 getName。第一个 class 适用于 toEqual,第二个 class 无效。

class Person01 {
    constructor(name) { this.name = name; }
    getName() { return this.name; }
}

class Person02 {
    constructor(name) { this.name = name; }
    getName = () => { return this.name; }
}

const testCases = [
    [
        // passes
        new Person01('Alan', 'Kay'),
        new Person01('Alan', 'Kay'),
    ], 
    [
        // fails due to class properties
        new Person02('Alan', 'Kay'),
        new Person02('Alan', 'Kay'),
    ]
];

describe('when one class has the same values that another class has', () =>
    testCases.forEach(pair =>
        it('is considered to be equal to that class', () =>
            expect(pair[0]).toEqual(pair[1]))));

这是第二个 class 的失败消息。

Expected: {"firstName": "Alan", "getName": [Function anonymous], "lastName": "Kay"} 
Received: {"firstName": "Alan", "getName": [Function anonymous], "lastName": "Kay"} 

我们目前的解决方法是 运行 JSON.parse(JSON.stringify(obj)) 实际值和预期值。

我们正在寻找的是 toEqual 的变体,它对 class 属性的工作方式与对 class 方法的工作方式相同。

这是我们的 babel.config.js 文件。

module.exports = function (api) {

  api.env();

  const plugins = [
    "@babel/proposal-class-properties",
  ];

  return {
    plugins,
  };
}

问题是每个实例都会创建函数 class 属性...

...因此 toEqual 失败,因为每个实例都有一组不同的函数属性。


一个选项是创建 custom matcher, but that is tricky since toEqual is doing a lot

另一种选择是在使用之前过滤函数属性 toEqual:

const filterFunctions = (obj) => 
  Object.keys(obj)
    .filter(k => typeof obj[k] !== 'function')
    .reduce((a, k) => { a[k] = obj[k]; return a; }, {});

describe('when one class has the same values that another class has', () =>
  testCases.forEach(pair =>
      it('is considered to be equal to that class', () =>
          expect(filterFunctions(pair[0])).toEqual(filterFunctions(pair[1])))));  // Success!