Cucumber-js: Возможность ослабить проверку аргументов для определений шагов

Созданный на 12 февр. 2016  ·  14Комментарии  ·  Источник: cucumber/cucumber-js

Привет! Хотя я понимаю ценность строгой проверки, которая выполняется для аргументов шага, я также чувствую, что необходимо передать этот элемент управления обратно пользователям.

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

Я хотел бы предложить добавить еще один параметр конфигурации к объекту параметров, который может быть назван чем-то вроде skipStrictParameterCheck, который, если он не установлен, будет считаться ложным. Таким образом, для наиболее распространенного использования поведение по умолчанию будет заключаться в строгой проверке, но для тех, кто хочет использовать фреймворк для создания чего-то большего, он дает им гибкость для использования некоторых динамических возможностей JavaScript.

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

Я сделал такую ​​же просьбу, см. №445 :)

@ riaan53 , да, я посмотрел на это, и, к сожалению, запрос был отклонен, возможно, по некоторым из правильных причин. Хотя я не согласен с рассуждениями, я действительно думаю, что как часть построения более крупной структуры эта функция важна до тех пор, пока пользователи не злоупотребляют ею, поэтому я рекомендую сделать это строго необязательной частью конфигурации.

О какой общей логике обработки вы говорите?

Конечно. В одном случае я хочу, чтобы все разработчики могли использовать ссылки на выражения в определениях своих шагов. например, «Когда пользователь $ {admin} входит в систему».

Теперь, в этом случае $ {admin} будет разрешен, возможно, из файла JSON, и ответственность за разрешение будет лежать на разработчике при реализации определения шага. Однако если вы действительно посмотрите на такое разрешение свойств, это можно сделать с помощью общего кода, при котором разработчик вообще не знает об этом.

Для этого я могу легко создать универсальную оболочку функции вокруг реализаций шага разработчика, которая будет принимать необработанные аргументы, введенные Cucumber, разрешать их, а затем вводить разрешенные значения в фактическую реализацию шага.

В настоящее время я не смогу написать такую ​​универсальную функцию, поскольку проверки Cucumber будут гарантировать, что функция имеет правильное количество параметров, которые в моем случае не будут такими, как моя универсальная функция-оболочка не будет принимать именованные аргументы, и я бы использовал объект аргументы.

Надеюсь, я смог понять. В нашем текущем проекте есть и другие варианты использования, когда потребуется универсальная функция-оболочка для реализаций шагов.

Если бы мы реализовали преобразование аргументов шага, это решило бы вашу проблему: https://github.com/cucumber/cucumber/wiki/Step-Argument-Transforms?

Ах да, потенциально может решить проблему эталонного разрешения. Однако у меня все еще есть необходимость поместить универсальную функцию-оболочку вокруг реализаций шагов по другим причинам.

Например, одна из причин заключается в том, что я использую Protractor с Cucumber в качестве фреймворка, а в Protractor нужно зарегистрировать пользовательские обещания в потоке управления WebDriver, чтобы он усердно ждал, пока эти пользовательские обещания будут разрешены, прежде чем переходить к следующему шагу.

Хотя Cucumber действительно ожидает выполнения обещания (если возвращается из реализации шага), очевидно, что он не знает WebDriver, и чаще всего нам нужно добавлять дополнительный код в реализацию каждого шага, чтобы зарегистрировать возвращенное обещание с помощью WebDriver.

Это снова очень легко решить с помощью общей оболочки функции, для которой проверка параметров должна быть ослаблена с помощью Cucumber.

Я уже пару раз сталкивался с этой проблемой.

Самый известный из них - реализация помощника retry на каждом шаге Then :

cucumber.Then = function(match, callback) {
  const retryingCallback = (...args) => retry(async () => await callback(...args));
  cucumber.Then(match, retryingCallback);
};

Я использую этот помощник для решения проблем с синхронизацией на конечном сервере. По сути, он выполняет обратный вызов каждые x секунд, пока не пройдет y секунд или не пройдет обратный вызов.

К сожалению, это вызывает

function has 0 arguments, should have 1 (if synchronous or returning a promise) or 2 (if accepting a callback)

Альтернатива, которую я использую сейчас, - это вызов помощника на каждом шаге Then , что вызывает БОЛЬШОЕ дублирование кода.

this.Then(/^I see that "([^"]*)" does not have a destination$/, async clientName => {
  return retry(async () => {
    const client = await homeView.clientByName(clientName);
    expect(client.destinationName).to.not.exist;
  });
});

Другой случай, более простой, когда мне нужна вспомогательная функция для входа в систему.

function loggedIn(username, func) {
  return (...args) => {
    await accounts.login(username);
    return func(...args)
  };
}

this.Then(/^someone logged in as "([^"]*)" sees a destination named "([^"]*)"$/, loggedIn(assert.destinationExists));

Также это избавит меня от большого количества дублирования кода.

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

PS Помощник повтора:

const patience = 250;
const interval = 5;

function delay(time) {
  return new Promise(function (fulfill) {
    setTimeout(fulfill, time);
  });
}

async function attempt(start, func) {
  const attemptDate = new Date();
  try {
    return await func();
  } catch (errr) {
    const timeElapsed = attemptDate.getTime() - start.getTime();
    if (timeElapsed < patience) {
      await delay(interval);
      return await attempt(start, func);
    } else {
      throw errr;
    }
  }
}

export async function retry(func) {
  const start = new Date();
  return await attempt(start, func);
}

_Редактировать_

Пытался обойти это:

function splat(func) {
  return (one, two, three, four, five, six, seven, eight, nine, ten) => {
    if (typeof ten !== 'undefined') {
      return func(one, two, three, four, five, six, seven, eight, nine, ten);
    } else if (typeof nine !== 'undefined') {
      return func(one, two, three, four, five, six, seven, eight, nine);
    } else if (typeof eight !== 'undefined') {
      return func(one, two, three, four, five, six, seven, eight);
    } else if (typeof seven !== 'undefined') {
      return func(one, two, three, four, five, six, seven);
    } else if (typeof six !== 'undefined') {
      return func(one, two, three, four, five, six);
    } else if (typeof five !== 'undefined') {
      return func(one, two, three, four, five);
    } else if (typeof four !== 'undefined') {
      return func(one, two, three, four);
    } else if (typeof three !== 'undefined') {
      return func(one, two, three);
    } else if (typeof two !== 'undefined') {
      return func(one, two);
    } else if (typeof one !== 'undefined') {
      return func(one);
    } else {
      return func();
    }
  };
}

cucumber.Then = function(match, callback) {
  const retryingCallback = splat((...args) => retry(async () => await callback(...args)));
  cucumber.Then(match, retryingCallback);
};

но

function has 10 arguments, should have 1 (if synchronous or returning a promise) or 2 (if accepting a callback)

делая меня грустной пандой

Вот пример упаковки функции для сохранения длины. Было бы неплохо, если бы был только крошечный модуль узла, который сделал бы это за вас.

Спасибо за предложение! Это сработает, если вы укажете количество аргументов на определение шага, верно?

например.

this.Then(/^someone logged in as "([^"]*)" sees a destination named "([^"]*)"$/, createProxy(loggedIn(assert.destinationExists), 2));

Самая важная проблема для меня - это невозможность добавить универсальное промежуточное ПО для многоэтапных определений. Что-то вроде createProxy могло бы сработать, если бы я превратил его в объект, который позволяет регистрацию промежуточного программного обеспечения, а затем сообщал бы ему количество аргументов в каждом отдельном определении шага. (внимательно посмотрите на мой первый пример, и вы увидите, что я не могу напрямую использовать createProxy , потому что функция retry обернет его. Должно быть наоборот, но тогда createProxy не будет знать количество аргументов для каждого обратного вызова)

Тем не менее, все еще кажется очень неудобным по сравнению с возможностью отключить ошибку. :невиновный:

Я думаю, вы можете использовать вариант этой функции, где вместо передачи proxyLength вы просто передаете функцию, которую вы обертываете, и используете function.length .

Отличное предложение, спасибо!

У меня есть что-то вроде следующего для работы:

cucumber.Then = function(match, callback) {
  cucumber.Then(match, retryProxy(callback));
};

function retryProxy(func) {
  const numberOfArgs = func.length;
  switch (numberOfArgs) {
    case 0: return () => retry(func);
    case 1: return (a) => retry(func, a);
    case 2: return (a, b) => retry(func, a, b);
    case 3: return (a, b, c) => retry(func, a, b, c);
    case 4: return (a, b, c, d) => retry(func, a, b, c, d);
    case 5: return (a, b, c, d, e) => retry(func, a, b, c, d, e);
  }
}

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

Рад, что теперь я могу добавить промежуточное ПО без каких-либо изменений в определения шагов!

@thomasvanlankveld Просто чтобы вы знали, я нашел эту библиотеку, которая обертывает функцию, чтобы дать ей определенную длину функции: https://github.com/blakeembrey/arity

@ sushil-rxr не могли бы вы что-нибудь вроде в вашей общей оболочке функции, чтобы сохранить исходную длину функции?

В 2.0.0-rc.1 теперь можно добавить универсальную оболочку функции. Он также имеет встроенную функцию сохранения исходной длины функции.

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

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