Sinon: Непредвиденный сбой теста после выпуска v7.2.0

Созданный на 23 июл. 2019  ·  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. Используйте любую версию Sinon выше 7.1.1
  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 (который использует "2.1.3" из samsam ), и это работает.
Версия 7.2.0 не работает (используется 3.3.3 из samsam ).
Самая последняя версия 9.2.3 также не работает (использует 5.3.0 из samsam ).

Я нахожусь на узле 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 рейтинги