Sinon: v7.2.0のリリース後に予期せずテストが失敗する

作成日 2019年07月23日  ·  11コメント  ·  ソース: sinonjs/sinon

バグを説明する
Sinonを最新のv7リリースにアップグレードすると、予期しない障害が発生します。 私が特定できた唯一の関連する変更は、このPRでした:#1955- deep-equal.jssamsam.deepEqual置き換えます。
テストは7.2.0までのすべてのバージョンに合格し、7.1.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件

全体的に素晴らしいバグレポートをありがとうございます。 ただし、大きな欠点が1つあります。検証やバグの追跡のためにサンプルを実行することはできません。 それ自体では実行できず、再現する方法がなければ、これを修正するのは困難です。

生殖のための最小限の候補を作ることができますか? var myObject = { aMethod: function(){}, aProp: 42 }; // etcような単純化のように、元の例全体を複製する必要はありません。

これが壊れていることを知っていれば、もちろんメジャーバージョンをアップしていたので、回帰テストは素晴らしいでしょう!

PS sinon-chaiも関係しているようです。 それはリストする価値があります。

@ fatso83複製のために、分離された実行可能な例を取得する方法を見てみましょう

更新はありますか?

この問題は、最近のアクティビティがないため、自動的に古いものとしてマークされています。 それ以上のアクティビティが発生しない場合は閉じられます。 貢献していただきありがとうございます。

@mantoniあなたのプロジェクトの1つでこのようなことが起こったことを報告しませんでしたか?

更新がないことをお詫び申し上げます。問題が含まれていたプロジェクトとは関係がなくなりました。 他の誰かが同じ問題を経験している場合、彼らが再現の例を提供できれば素晴らしいでしょう

@ 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);
});

私はこれをsinon7.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 評価