开玩笑:期望对象没有属性

joe*_*ain 13 javascript matcher jestjs

我想编写一个测试来断言给定对象不具有某些属性。

说我有一个函数

function removeFooAndBar(input) {
  delete input.foo;
  delete input.bar;
  return input;
}
Run Code Online (Sandbox Code Playgroud)

现在我想写一个测试:

describe('removeFooAndBar', () => {
  it('removes properties `foo` and `bar`', () => {
    const data = {
      foo: 'Foo',
      bar: 'Bar',
      baz: 'Baz',
    };
    expect(removeFooAndBar(data))
      .toEqual(expect.objectContaining({
        baz: 'Baz', // what's left
        foo: expect.not.exists() // pseudo
        bar: undefined // this doesn't work, and not what I want
      }));
  });
});
Run Code Online (Sandbox Code Playgroud)

断言这一点的正确方法是什么?

Beh*_*oth 16

评论区讨论后更新

您可以使用expect.not.objectContaining()。这种方法工作正常,但有一个不幸的边缘情况:它在属性存在但为undefinedor时匹配null。要解决此问题,您可以显式添加要包含在检查中的这些值。您需要匹配器jest-extended的包toBeOneOf()

expect({foo: undefined}).toEqual(expect.not.objectContaining(
    {foo: expect.toBeOneOf([expect.anything(), undefined, null])}
));
Run Code Online (Sandbox Code Playgroud)

嵌套 prop 失败的示例:

const reallyAnything = expect.toBeOneOf([expect.anything(), undefined, null]);

expect({foo: undefined, bar: {baz: undefined}}).toEqual(
    expect.not.objectContaining(
        {
            foo: reallyAnything,
            bar: {baz: reallyAnything},
        }
    )
);
Run Code Online (Sandbox Code Playgroud)

原答案

我要做的是显式检查该对象是否具有名为bar或 的属性foo

delete data.foo;
delete data.bar;
delete data.nested.property; 

expect(data).not.toHaveProperty('bar');
expect(data).not.toHaveProperty('foo');
expect(data.nested).not.toHaveProperty('property');
// or
expect(data).not.toHaveProperty('nested.property');
Run Code Online (Sandbox Code Playgroud)

或者通过循环要删除的属性来减少重复。

const toBeRemoved = ['foo', 'bar'];

toBeRemoved.forEach((prop) => {
    delete data[prop];
    expect(data).not.toHaveProperty(prop);
});
Run Code Online (Sandbox Code Playgroud)

然而,循环方法对于可能的嵌套对象来说并不是太好。我相信你正在寻找的是expect.not.objectContaining()

expect(data).toEqual(expect.not.objectContaining({foo: 'Foo', bar: 'Bar'}));
Run Code Online (Sandbox Code Playgroud)

expect.not.objectContaining(object)匹配任何不递归匹配预期属性的接收对象。也就是说,预期对象不是接收到的对象的子集。因此,它匹配接收到的对象,该对象包含预期对象中不存在的属性。-笑话文档


joe*_*ain 9

这个答案是对已接受答案的释义。添加它只是因为对已接受答案的这一确切建议被拒绝。

bar您可以显式检查该对象是否具有名为或 的属性foo

delete data.foo;
delete data.bar;

expect(data).not.toHaveProperty('bar');
expect(data).not.toHaveProperty('foo');
Run Code Online (Sandbox Code Playgroud)

对于嵌套属性:

delete data.nested.property; 

expect(data.nested).not.toHaveProperty('property');
// or
expect(data).not.toHaveProperty('nested.property');
Run Code Online (Sandbox Code Playgroud)

或者通过循环要删除的属性来减少重复。

const toBeRemoved = ['foo', 'bar', 'nested.property'];

toBeRemoved.forEach((prop) => {
    expect(data).not.toHaveProperty(prop);
});
Run Code Online (Sandbox Code Playgroud)

然而,循环方法对于可能的嵌套对象来说并不是太好。您正在寻找的是expect.not.objectContaining().

expect({baz: 'some value'}).toEqual(expect.not.objectContaining(
    {foo: expect.anything()}
));
Run Code Online (Sandbox Code Playgroud)

这种方法工作正常,但有一个不幸的边缘情况:它在属性存在时匹配,但是undefinedor null

expect({foo: undefined}).toEqual(expect.not.objectContaining(
    {foo: expect.anything()}
));
Run Code Online (Sandbox Code Playgroud)

也会匹配。要解决此问题,您可以显式添加要包含在检查中的这些值。您需要匹配器jest-extended的包toBeOneOf()

expect({foo: undefined}).toEqual(expect.not.objectContaining(
    {foo: expect.toBeOneOf([expect.anything(), undefined, null])}
));
Run Code Online (Sandbox Code Playgroud)

一个嵌套 props 的例子,预计会失败:

const reallyAnything = expect.toBeOneOf([expect.anything(), undefined, null]);

expect({foo: undefined, bar: {baz: undefined}}).toEqual(
    expect.not.objectContaining(
        {
            foo: reallyAnything,
            bar: {baz: reallyAnything},
        }
    )
);
Run Code Online (Sandbox Code Playgroud)