Sinon: 沙盒抛出错误“无法存根不存在的自有财产”

创建于 2017-08-18  ·  14评论  ·  资料来源: sinonjs/sinon

我只是尝试从2.4.1升级到3.2.1,然后遇到以下问题。 该代码在2.4.1中有效:

        const spy = sandbox.spy();
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: spy
            }
        });

但是在3.2.1中,它引发了一个异常: TypeError: Cannot stub non-existent own property google

迁移指南中未提及它,因此它似乎是一种回归。

Bug Regression

最有用的评论

〜谢谢您恢复这种行为〜

想要添加一个支持存根不存在属性的用例。

在我的用例中,我在配置对象上添加了一个属性。 config对象具有各种可选键,并通过从开发人员的机器中加载文件来初始化。 当我运行特定的测试时,我需要将这些键之一设置为一个已知值,然后想要按原样还原开发人员的对象。

sandbox.stub(serverSecrets, 'the_key_i_need_set').value(fakeValue)是传达这一点的非常清晰的方法。 即使我在运行时不知道键是否为set_,也可以得到相同的行为,这是很好的。

所有14条评论

与#1512相关。

如果该属性不存在,则无需将其添加到沙箱中。 只需覆盖它。 但是,是的,如果以前可行,并且我们还没有明确表示应该更改,那么这是一种回归。

不知道我们应该在这里做什么。 更新文档,说存根不存在的值是没有意义的,不受支持,还是有可能?

如果该属性不存在,则无需将其添加到沙箱中。 只需覆盖它。

将属性添加到沙箱的好处是,sinon然后可以帮助我通过sandbox.restore()在每次测试之间保持全局测试环境的清洁。 这是一项非常有用的功能,尤其是在处理我无法控制API的第三方库(例如Google Maps)时。 如果可以使其在3.x行中工作,那就太好了。

我也只是注意到我犯下了不提供完整例子的罪过。 我的沙盒是用2.4.1格式创建的:

let sandbox;

before(() => { sandbox = sinon.sandbox.create(); })
afterEach(() => { sandbox.restore(); })

不知道这是否重要; 对于未能尽快提供该产品,我们深表歉意。

我认为在@ZebraFlesh所描述的场景中,我宁愿使文本内容更加明确。

// not so explicit, doesn't work with [email protected]
beforeEach(function() {
    const spy = sandbox.spy();
    sandbox.stub(window, 'google').value({
        maps: {
            LatLng: x => x,
            Map: spy
        }
    }); 
});
// more explicit, works with sinon<strong i="9">@2</strong>, sinon<strong i="10">@3</strong>
function setGoogleMapsFixture(sandbox) {
    window.google = {
        maps: {
            LatLng: x => x,
            Map: sandbox.spy()
        }
    };
}

function removeGoogleMapsFixture() {
    delete window.google;
}

beforeEach(function() {
    setGoogleMapsFixture(sandbox)
});

// not using afterEach, as this only needs to happen
// after the last test in this block is run
after(function() {
    removeGoogleMapsFixture();
});

有了上面概述的灯具的更明确的设置,您就不需要在Sinon中使用允许对不存在的自有属性进行存根的功能。

不知道我们应该在这里做什么。 更新文档,说存根不存在的值是没有意义的,不受支持,还是有可能?

虽然我认识到在某些情况下可能很方便(例如@ZebraFlesh所描述的情况),但我认为对不存在的自身属性进行存根可能会导致测试错误,因为作者输入了错误的名称,因此测试通过了他们打算存根的现有财产。 我们的目标应该是尽可能避免发生错误的可能性,而又不要太过严格。

我认为,对不存在的自己的属性进行存根处理应该不受支持。 我们应该更新文档。

@mroderick我同意您的

所以:

  • 迅速删除沙盒检查以修复此中断功能

或/和(?)

  • 删除普通和沙盒存根的功能

    • 使用更新的文档发布新的主要版本

有了上面概述的灯具的更明确的设置,您就不需要在Sinon中使用允许对不存在的自有属性进行存根的功能。

我认为,如果您的测试装置在测试之间永不变化,那会很好。 但是,我的装置却可以。 一个简单的示例涵盖成功和失败案例:

it('handles the success case', () => {
        const spy = sandbox.spy();
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: spy
            }
        });
        // ... test, including asserting that the spy was called
});

it('handles the failure case', () => {
        const msg = 'test error';
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: sandbox.stub().throws(new Error(msg))
            }
        });
        // ... test, ignoring spy calls and instead focusing on error handling
});

2.x中的行为的优点在于,每次测试后,通过sandbox.restore()可以正确清理所有内容。 使用上面概述的更明确的灯具设置示例,我想您可以删除afterEach挂钩中的非自有属性,以实现相同的效果。

为了解决由于无意中输入现有属性的名称而导致潜在错误的问题,sinon可以修改公共API:

  • stub.ownValue() :存根仅拥有属性,非拥有属性抛出
  • stub.value() :仅对非自有属性进行存根,对自有属性进行抛出

API变得更加明确,消费者被迫为手头的任务选择合适的工具。

这与#1508中的讨论非常相关(尽管它处理的是普通存根),其中@lucasfcosta持有相反的观点-我们不应该为undefined属性抛出。 无论我们落在何处,我都坚信_对于常规存根和沙箱来说,在存根API中我们需要保持一致。 我们不应该在一种情况下支持它,在另一种情况下也不应该支持。

目前的情况是:

  • 普通的存根曾经用于1.x,但是在2.0中有所改变,现在不再抛出
  • 沙箱不用于抛出,而是在3.1(?)中开始抛出

因此,有一阵子我们使用了功能奇偶校验,但是后来又又失去了它。我认为这种曲折的形式对用户没有太大好处,因此我们应该进行此讨论。 尽管我确实同意Morgan的意见,因为它可能会进行更具体的测试,但我不喜欢删除两个主要版本的行为,然后再次添加它。 我认为这样做只会带来最少的噪音(针对客户的修复,此跟踪器上的问题/问题),仅用于还原此回归。

尽管我理解了不便之处,但似乎有一个简单的解决方法,只需最少的代码更改即可。

before(function() {
  window.google = 'This is a placeholder for sinon to overwrite.';
});

after(function() {
  delete window.google;
});

这使正弦代码保持不变。

回归与不良文档

这似乎是预期的行为,因为已经对其进行了测试。 我们应该更新文档以反映我的观点。 它具有重大意义,因此可以容忍重大变化。

@fearphage保持现状意味着对沙箱不支持存根行为,而对于常规存根

解决方案在#1557中实现

我已经阅读了各种线程,我明白了为什么会发生这种情况,但是在Typescript中,真正的痛苦是您经常在类原型上实现函数,在这种情况下,即使一切看起来都很好,sinon也会吐出虚拟对象明智的做法(因为keyof YourType会很高兴地允许所有在原型链下定义的公共函数)。

我知道Typescript可能不是你们的优先事项,但是即使在JS中,似乎也很直观地知道myObject.callMe()会完美地执行,而sinon.stub(myObject, "callMe")在这种情况下则不会。 我宁愿不必去研究如何将特定对象组合在一起,这样我就知道如何对其进行存根。

考虑到类在JS中获得了更多的本机支持,我真的认为这是一个重要的用例,可以为您提供一条愉快的道路。

如果您收到一条错误消息,指出该方法未在对象上定义,则您知道该错误可能是由于原型造成的。 然后,直接使用myObject.callMe = sinon.stub();修改对象似乎不太麻烦,恕我直言...还应避免创建清理/拆卸函数,因为原型从未更改。

是的,我想这并不是很难解决的问题,它只是给我增加了认知负担,以了解事情是如何实现的。

这似乎也出乎意料,因此我感到有必要在测试中添加注释,以解释为什么同一对象的连续两行的存根代码不同,以及为什么我手动删除了拆解中的其中一个存根,但另一个由沙箱处理。

〜谢谢您恢复这种行为〜

想要添加一个支持存根不存在属性的用例。

在我的用例中,我在配置对象上添加了一个属性。 config对象具有各种可选键,并通过从开发人员的机器中加载文件来初始化。 当我运行特定的测试时,我需要将这些键之一设置为一个已知值,然后想要按原样还原开发人员的对象。

sandbox.stub(serverSecrets, 'the_key_i_need_set').value(fakeValue)是传达这一点的非常清晰的方法。 即使我在运行时不知道键是否为set_,也可以得到相同的行为,这是很好的。

此页面是否有帮助?
0 / 5 - 0 等级