Sinon: Шпион `returnValue` не работает с генераторами

Созданный на 3 янв. 2015  ·  20Комментарии  ·  Источник: sinonjs/sinon

При работе с функциями генератора возвращаемое значение шпиона всегда равно undefined . Вот неудачный тестовый пример:

require('should');

var sinon = require('sinon');
var co = require('co');

var foo = {
  bar: function() {
    return 'bar';
  },
  biz: function *() {
    return 'biz';
  }
}

describe('Return value', function() {
  it('should work with regular functions', function() {
    sinon.spy(foo, 'bar');

    foo.bar();

    foo.bar.firstCall.returnValue.should.equal('bar');
  });

  it('should work with generator functions', co.wrap(function*() {
    sinon.spy(foo, 'biz');

    var result = yield foo.biz();

    result.should.equal('biz');
    foo.biz.firstCall.returnValue.should.equal('biz');
  }));
});

Беги с npm install co mocha should sinon && ./node_modules/.bin/mocha --harmony index.js .

2.x Unverified

Все 20 Комментарий

Sinon пока не поддерживает функции ES6. Работа над этим еще не началась, поэтому я не знаю, когда это сработает.

@mantoni , верно, это был скорее запрос функции :) вы хотите оставить его открытым, например, с каким-то тегом («es6»?), чтобы при добавлении его в дорожную карту было легче повторно -оцените, что нужно сделать?

Это было бы отличной функцией.

@ruimarinho я нашел это https://github.com/ingameio/SinonES6.JS
замена require("sinon") на require("sinon-es6"), кажется, делает ваши тесты пройденными

--
отредактировано @fatso83 22 июня 2017 года:
Это не похоже на правду. Я вручную проверил это. Он все равно терпит неудачу, что имеет смысл, поскольку sinon-es6 _only реализует поддержку генераторов для имитации _.

Кто-нибудь работает над этим?

@emgeee я не в курсе.

+1 @ruimarinho sinon.js с функциями es6 было бы здорово!

@ingameio хотите сделать PR с вашими изменениями?

@fatso83 спасибо за предложение!
Скоро сделаю пиар ;)

У меня возникают проблемы при запуске buster-test для упакованной версии. Я провел некоторую отладку, но в эти дни у меня мало времени, поэтому я не смог решить проблему.
Если кто-то хочет помочь, я могу внести изменения в свою вилку, просто скажите мне, и я это сделаю.

@gaguirre Это отличная новость. Максимилиан только что преобразовал все тесты в Mocha и внес некоторые изменения в настройку тестов, так что, может быть, все получится, если вы вытащите последние изменения из master ?

@gaguirre На случай, если кто-то наткнется на эту ветку, я думаю, что было бы неплохо внести изменения в вашу вилку, чтобы прохожий мог ее посмотреть.

@ fatso83 Я отправил первую версию в underscope/sinon .
Основная проблема возникает при запуске тестов с движком, который не поддерживает es6 (в данном случае phantomjs): происходит сбой при синтаксическом анализе кода, поэтому я использую eval() в качестве обходного пути, поскольку он может быть окружен попыткой/ поймать. Я думаю, что это неприемлемо, но это отправная точка.

Прямо сейчас я пытаюсь обернуть вызов генератора в другой файл и потребовать его динамически, но он не работает, я думаю, потому что в браузере он объединяет даже динамически необходимые файлы. Мне интересно, можно ли исключить некоторые файлы при запуске npm run test-headless .

Как вы думаете, какое решение может быть лучшим?

Спасибо за отзыв, @gaguirre . Таким образом, основная проблема заключается в том, что нам нужен какой-то способ избежать синтаксического анализа, если движок его не поддерживает. Это немного сложнее, чем простое обнаружение функций, как мы делаем с WebWorkers и т. д.

Просто делегировать фактическую проверку так же просто, как if(require('generator-support')){ ... } , но это не устраняет фактическую проблему синтаксического анализа.

Любые идеи о том, как с этим справиться, @mantoni и @mroderick?

PS Через несколько часов я уезжаю в офлайн-каникулы, так что прочитать ответы смогу только после Пасхи.

Я не могу сказать, действительно ли работает этот уровень заглушки генератора (особенно в сценариях, ориентированных на сопрограммы) - кажется, что некоторая эмуляция асинхронного поведения может быть в порядке. Но я думаю, что есть способ обойти проблему синтаксического анализа.

Предложение :

Извлеките код генератора в отдельный файл и require() этом файле только по запросу при обертывании функции генератора, т.е.:

?/es6-support.js:

"use strict";

exports.getGeneratorWrapper = function(method, ctx, args) {
    return function* () {
        return mockObject.invokeMethod(method, ctx, args);
    }
}

lib/sinon/mock.js->ожидает...:

var wrapper = function () {
    return mockObject.invokeMethod(method, this, arguments);
});

if (/^function\s*\*/.test(method.toString())) {
    wrapper = require('es6-support').getGeneratorWrapper(method, this, arguments);
}

wrapMethod(this.object, method, wrapper);

(Также сделайте то же самое с модульными тестами и убедитесь, что отслеживание покрытия кода не приводит к автоматическому включению файла «es6-support».)

Альтернативное предложение :

Будет ли совместимость с ES5 по-прежнему полезной целью к тому времени, когда sinon 2.0 будет готов к выпуску? Возможно, пришло время сказать тем немногим людям, которые все еще поддерживают устаревшие среды только для ES5 без использования транспиляторов, что они останутся на 1.x.

@evan-king Это условное требование не является решением, так как оно сломается в наших сборках браузера. Поскольку условные требования побеждают статический анализ графа зависимостей, такие инструменты, как WebPack, Rollup и Browserify, не смогут с этим справиться. Он либо полностью включен, либо полностью исключен.

И да, совместимость с ES5 — это важно. Поддержка генератора ES6 в лучшем случае некачественная, и принуждение людей к использованию дополнительных инструментов для использования Sinon в своих проектах поднимет планку для проведения тестирования. И для многих этот порог и так достаточно высок. Имхо, возможность просто включить тег script, чтобы заставить его работать, является важной функцией. Возможно, когда-нибудь мы нарушим совместимость с ES5, но этого нет в нашей дорожной карте и даже не обсуждалось для Sinon 3. Фактически, Sinon 2 отсутствует уже довольно давно; есть только некоторые незначительные неудобства, мешающие нам сделать официальный релиз.

В основном есть два способа получить совместимость с ES6 без нарушения существующей совместимости среды выполнения ES5, которые я могу придумать, и я не совсем уверен в последнем (не тестировал его):

Условная оценка модулей

Это хак, но довольно простой. Это означает, что мы можем полагаться на встраивание статических ресурсов и тестирование функций ES6, чтобы решить, следует ли оценивать требуемый модуль. Это обеспечит совместимость с ES5 (Sinon будет исправлен только в том случае, если среда выполнения поддерживает синтаксис), нет необходимости в дополнительных инструментах в виде Babel ни на стороне создателя библиотеки, ни на стороне клиента, а тестирование ES6 сломает браузеры ES5, которые потерпел бы неудачу в любом случае.

Предполагая, что что-то вроде brfs установлено, код будет выглядеть примерно так:

_runtime-features.js_

var features = {};
try { 
    new Function("var foo = function* foo(){ }") ;
    features.generatorSupport = true; 
} 
catch(err){ features.generatorSupport = false; }
module.exports = features;

_es6-генератор-обертка.js_

return function* () {
    return mockObject.invokeMethod(method, ctx, args);
}

_es6.js_

var features = require('./runtime-features');

if( features.generatorSupport ) {
    var code = fs.readFileSync('./es6-generator-wrapper.js');
    module.exports.getGeneratorWrapper = new Function("mockObject", "method", "ctx", "args", code);
} else {
    module.exports.getGeneratorWrapper = function() { throw new TypeError("You tried using a generator function in a runtime only capable of running ES5 code"); }
}

Величание Синон

Это тот, в котором я не уверен, так как я на самом деле не пробовал. Если мы транспилируем сборку через Babel в ES5, мы можем писать код ES6 повсюду, избегая прыгать через обручи, как я сделал выше, и мы все еще можем использовать те же виды проверок для генераторов. Они просто будут реализованы с использованием конструкций ES5. Конечно, тестирование ES6 в браузерах ES5 все равно не удастся. Это имеет те же преимущества, что и предыдущее, на стороне клиента, но мы можем помешать вкладу, поскольку знания ES2015 о таких вещах, как yield , async , function*() далеки от достижения широкая аудитория.

+1

@rpavlovs , нет смысла добавлять +1 в ветку. Пользовательский интерфейс GitHub имеет кнопку «Добавить реакцию» над каждым комментарием, если вам нужно выразить свои эмоции. +1 ничего не даст. С другой стороны, запрос на вытягивание, который реализует одно из приведенных выше предложений (или что-то более умное), имеет гораздо больше шансов решить эту проблему 😄

Я хотел бы получить информацию о том, как будет выглядеть поддержка API для генераторов , так как, потратив на это пару часов, я все еще не совсем уверен, что люди хотели бы видеть.

Чтобы начать конкретизировать это, я создал новую ветку , которая содержит модификации @ingameio для макета API, в то же время не нарушая совместимость с ES5 (используя упомянутый выше хак).

Что меня немного раздражает, так это то, что я действительно не могу сказать, как протестировать исходные изменения Ingameio, поскольку ни один из тестовых примеров не работает — даже пример в форке не завершен, и я не могу заставить что-то ломаться до/после. изменения.

Генераторы — простые вещи: нежные синхронные существа, помнящие свое прошлое. Поэтому, пожалуйста, не засоряйте примеры co и другими вещами, которые не связаны, так как это усложняет понимание того, что нужно/не работает. Например, верхний пример довольно сложен, и он также, кажется, ошибается в том, что делает yield , поскольку он ожидает, что возвращаемое значение «выражения доходности» будет таким же, как «выдаваемое значение». result доходности — это значение, переданное генератору в next() ( MDN )

Я понимаю, что примеры с использованием co , вероятно, используют его только для того, чтобы иметь возможность использовать yield непосредственно в тесте Mocha, но просто оберните свой пример в IIFE или каким-либо другим способом добиться того же для ради большей ясности.

Это простой тест поддержки генераторов (работает в сегодняшнем Sinon):

require("should");

var sinon = require("../sinon");

var foo = {
    bar: function () {
        return "bar";
    },
    biz: function *() {
        return "biz";
    }
};

describe("generator support", function () {
    it('should work with generator functions',  function(){
        var spy = sinon.spy(foo, 'biz');

        var iterator = foo.biz();
        var result = iterator.next();

        result.value.should.equal('biz');
        result.done.should.equal(true);
        spy.firstCall.returnValue.should.be.an.Object();
        spy.firstCall.returnValue.next.should.be.a.Function();
    });
});

Итак, какие расширения API нам нужны?
Исходя из исходного теста, я предполагаю, что мы хотели бы увидеть что-то вроде

foo.biz.firstGeneratedValue.should.equal('biz');
или
foo.biz.generatedValue[0].should.equal('biz');
?

копия @ruimarinho

Я закрываю этот вопрос, так как в исходном тесте была ошибка, и я не вижу никаких проблем с обработкой генератора в Sinon.

Пожалуйста, присоединяйтесь к обсуждению того, как будет выглядеть API для работы с генераторами (и связанными с ними итераторами) в выпуске № 1467.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги