Sinon: v7.2.0发布后测试意外失败

创建于 2019-07-23  ·  11评论  ·  资料来源: sinonjs/sinon

描述错误
将Sinon升级到最新的v7版本会导致一些意外的故障。 我能够识别的唯一相关更改是此PR:#1955-将deep-equal.js替换samsam.deepEqual
测试可以通过7.2.0之前的所有版本,恢复到7.1.1(一个发行版)不会导致任何问题,并且所有测试都可以通过。

观察到错误:

AssertionError: expected addTracks to have been called with arguments [{ language: "en", trackContentType: "CEA608" }]
[[functionStub] { language: "en", trackContentType: "CEA608" }] [{ language: "en", trackContentType: "CEA608" }]

测试产生错误:

    it('should call getTextTracksManager, Track, and setCaptionLang', () => {
      sinonSandbox.stub(chromecastReceiver, 'setCaptionLang');
      chromecastReceiver.player.getTextTracksManager = sinonSandbox.stub()
        .returns({ addTracks: sinonSandbox.spy() });
      sinonSandbox.stub(cast.framework.messages, 'Track').returns(() => ({}));

      chromecastReceiver.loadCaptions('off');

      expect(chromecastReceiver.player.getTextTracksManager).to.have.callCount(1);
      expect(cast.framework.messages.Track).to.have.been
        .calledWith(3, cast.framework.messages.TrackType.TEXT);
      expect(chromecastReceiver.textTracksManager.addTracks).to.have.been.calledWith([{
        trackContentType: cast.framework.messages.CaptionMimeType.CEA608,
        language: 'en',
      }]);
      expect(chromecastReceiver.setCaptionLang).to.have.been.calledWith('off');
    });

重现
重现行为的步骤:

  1. 使用任何高于7.1.1的Sinon版本
  2. 运行样本测试(以上)
  3. 看到错误

预期行为
测试应该通过

屏幕截图
如果适用,请添加屏幕截图以帮助解释您的问题。

上下文(请完成以下信息):

  • 库版本:7.2.0+
  • 环境:macOS 10.14.5

请让我知道是否可以提供有关此的更多信息。

Bug Regression pinned

最有用的评论

@ fatso83

感谢您抽出宝贵的时间对此进行调查。 我按照您的要求进行了操作,并创建了一个可显示错误的Runkit演示。 只需注释/取消注释不同的require('sinon')语句,然后运行代码以查看错误。

https://runkit.com/danielkg/sinon-issue-reproducible-bug-template

我也将代码发布在这里,以防runkit由于任何原因而无法正常工作。

// Employs 'mini-mocha' to emulate running in the Mocha test runner (mochajs.org)
require("@fatso83/mini-mocha").install();
// const sinon = require('[email protected]'); // WORKS!
const sinon = require('[email protected]'); // FAILS!
// const sinon = require('sinon');       // FAILS!
const { assert } = require('@sinonjs/referee');

const SAMPLE_USER = {
    id: 1,
    name: 'Kid',
    age: 22,
    weight: 83.5,
    notes: [{ txt: 'abc' }, { txt: 'def' }, { txt: 'ghi' }],
};

class MyUser {
    constructor(id, name, age, weight, notes = []) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.weight = weight;
        this.notes = notes;
    }
}

const createFakeUser = overrides => ({
    id: '007',
    name: 'Bond',
    ...overrides,
});

class Talkie {
    talk(user, topic) {
        console.log('talked to', user.name, 'about', topic);
    }
}

const action = (userId, talkie) => {
    const { name, age, weight, notes } = SAMPLE_USER;
    const user = new MyUser(userId, name, age, weight, notes);

    talkie.talk(user, 'apples');

    return user;
}


describe('stubbing', () => {
    const talkie = new Talkie();

    afterEach(() => {
        sinon.restore();
    });

    beforeEach(() => {
        sinon.spy(talkie, 'talk');
    });

    it('is the same user', () => {
        const userA = action(1, talkie);
        const userB = createFakeUser(SAMPLE_USER);

        assert.equals(JSON.stringify(userA), JSON.stringify(userB));
    });

    it('should set the return value', () => {
        const userA = action(1, talkie);
        const userB = createFakeUser(SAMPLE_USER);

        sinon.assert.calledWith(talkie.talk, userB, sinon.match.string);
    });
});

如果您有时间,请再看一下。 谢谢你。

亲切的问候,
丹尼尔

所有11条评论

感谢您提供的错误报告。 但是,它有一个主要缺陷:我无法运行您的示例进行验证或跟踪错误! 它本身不能运行,并且没有复制的方法,很难解决。

您能为繁殖做一个最低限度的候选人吗? 您不必复制整个原始示例,更像是像var myObject = { aMethod: function(){}, aProp: 42 }; // etc这样的简化。

如果我们知道这是失败的,那么我们当然会升级主要版本,因此回归测试将是很棒的!

PS似乎也涉及sinon-chai 。 值得列出。

@ fatso83让我看看如何获​​得一个孤立的,可运行的示例进行复制

任何更新?

由于此问题最近没有活动,因此已被自动标记为陈旧。 如果没有其他活动发生,它将关闭。 感谢你的贡献。

@mantoni您是否没有报告在您的一个项目中遇到过类似情况?

抱歉,由于缺少更新,我不再与包含该问题的项目相关联。 如果其他任何人都可以遇到相同的问题,那么如果他们可以提供一个例子来进行复制,那将是很好的。

@ fatso83我不记得遇到过同样的问题。 我已经在几个项目中升级到最新的Sinon,没有任何问题。

由于无法复制而结束

很抱歉让这个问题从死里复活,但是我遇到了同样的问题。

_下面的代码已简化,以用于说明目的。_

在我的应用程序中,我有这个课程

class MyUser {
  // ... some user properties  
}

以后我会在像这样的Koa控制器函数中使用它

const mqClient = require('./mqClient');

// GET /userDetails/<id> endpoint
async function getUserDetailsController(req, res) {
  const user = new MyUser();

  // populate user with properties etc.
  user.id = '1';
  user.name = 'Hans Gruber';
  user.tasks = [{ id: '42', title: 'execute Nakatomi tower heist' }];

  // ... get more details ...

  // Notify other users about this user
  mqClient.notifyOthers(user);

  res.json({ ok: true, user });
}

在我的getUserDetailsController.test.js我做这样的事情:

const mqClient = require('./mqClient');

const createExpectedUser = overrides => {
  id: '1',
  name: 'Hans Gruber',
  tasks: [{ id: '42', title: 'execute Nakatomi tower heist' }],
  ...overrides
};

it('does not fail', async () => {
  sinon.spy(mqClient, 'notifyOthers');

  const expectedUser = createExpectedUser();

  await presetRequest // helper object to run the controller code
    .get('/userDetails/1')
    .expect(200);

  // This works with sinon 7.1.1.
  // This fails with sinon 7.2.0 and onwards.
  sinon.assert.calledWithMatch(mqClient.notifyOthers, expectedUser);
});

我使用sinon 7.1.1(使用samsam “ 2.1.3”)进行了测试,并且可以正常工作。
版本7.2.0失败(使用samsam 3.3.3)。
最新版本9.2.3也会失败(使用samsam 5.3.0)。

我在节点14.5.3上,但是这也在节点12.20.0上发生。

我通过手动比较user来比较控制器中的expectedUser和测试中的JSON.stringify ,它们是100%相等的。 从句法上唯一的区别是,控制器使用类,而测试使用对象文字。

有谁知道为什么会这样? 还是有人对如何解决这个问题有个好主意?

亲切的问候,
丹尼尔

恐怕只要我们没有可重复的案例来验证这一点,我们就无法证明我们有空闲时间来研究这个问题。 我没有什么可寻找的,也没有任何东西可以证明我是对的。

不过,我确实喜欢修复错误,因此,如果您可以花一些时间来尝试使我可以运行的东西,那就这样做吧! RunKit是执行此操作的出色服务: https ://runkit.com/fatso83/sinon-issue-reproducible-bug-template

@ fatso83

感谢您抽出宝贵的时间对此进行调查。 我按照您的要求进行了操作,并创建了一个可显示错误的Runkit演示。 只需注释/取消注释不同的require('sinon')语句,然后运行代码以查看错误。

https://runkit.com/danielkg/sinon-issue-reproducible-bug-template

我也将代码发布在这里,以防runkit由于任何原因而无法正常工作。

// Employs 'mini-mocha' to emulate running in the Mocha test runner (mochajs.org)
require("@fatso83/mini-mocha").install();
// const sinon = require('[email protected]'); // WORKS!
const sinon = require('[email protected]'); // FAILS!
// const sinon = require('sinon');       // FAILS!
const { assert } = require('@sinonjs/referee');

const SAMPLE_USER = {
    id: 1,
    name: 'Kid',
    age: 22,
    weight: 83.5,
    notes: [{ txt: 'abc' }, { txt: 'def' }, { txt: 'ghi' }],
};

class MyUser {
    constructor(id, name, age, weight, notes = []) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.weight = weight;
        this.notes = notes;
    }
}

const createFakeUser = overrides => ({
    id: '007',
    name: 'Bond',
    ...overrides,
});

class Talkie {
    talk(user, topic) {
        console.log('talked to', user.name, 'about', topic);
    }
}

const action = (userId, talkie) => {
    const { name, age, weight, notes } = SAMPLE_USER;
    const user = new MyUser(userId, name, age, weight, notes);

    talkie.talk(user, 'apples');

    return user;
}


describe('stubbing', () => {
    const talkie = new Talkie();

    afterEach(() => {
        sinon.restore();
    });

    beforeEach(() => {
        sinon.spy(talkie, 'talk');
    });

    it('is the same user', () => {
        const userA = action(1, talkie);
        const userB = createFakeUser(SAMPLE_USER);

        assert.equals(JSON.stringify(userA), JSON.stringify(userB));
    });

    it('should set the return value', () => {
        const userA = action(1, talkie);
        const userB = createFakeUser(SAMPLE_USER);

        sinon.assert.calledWith(talkie.talk, userB, sinon.match.string);
    });
});

如果您有时间,请再看一下。 谢谢你。

亲切的问候,
丹尼尔

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