Cucumber-js: خيار لتخفيف الحجج تحقق من تعريفات الخطوة

تم إنشاؤها على ١٢ فبراير ٢٠١٦  ·  14تعليقات  ·  مصدر: cucumber/cucumber-js

مرحبًا ، على الرغم من أنني أفهم قيمة الفحص الصارم الذي يتم إجراؤه على وسيطات الخطوة ، إلا أنني أشعر أيضًا أن هناك حاجة إلى إعادة هذا التحكم إلى المستخدمين.

على سبيل المثال ، في حالتي ، أرغب في تنفيذ وظائف الغلاف العامة حول وظائف تنفيذ الخطوة حيث أريد إضافة بعض منطق المعالجة العام قبل وبعد استدعاء الخطوة. بالنسبة إلى هذا المُجمّع العام ، أحتاج إلى الوصول إلى أي وسيطات تم تمريرها ، وبالتالي أحتاج إلى الوصول إلى مصفوفة الوسائط بدلاً من الإعلان عن أي معلمات صريحة. حاليًا ، سيمنعني الخيار من القيام بذلك لأنه يقوم بفحص صارم للمعلمات.

أود أن أقترح إضافة معلمة تكوين أخرى إلى كائن الخيارات يسمى ربما شيء مثل skipStrictParameterCheck والذي إذا لم يتم تعيينه فسيتم افتراضه على أنه خطأ. بهذه الطريقة بالنسبة للاستخدام الأكثر شيوعًا ، سيكون السلوك الافتراضي عبارة عن فحوصات صارمة ، ولكن بالنسبة للآخرين الذين يرغبون في استخدام إطار العمل لبناء شيء أكثر حوله ، فإنه يمنحهم المرونة للاستفادة من بعض الإمكانات الديناميكية لجافا سكريبت.

ال 14 كومينتر

لقد قدمت نفس الطلب ، انظر # 445 :)

@ riaan53 ، نعم لقد

ما هو منطق المعالجة العامة الذي تتحدث عنه؟

بالتأكيد. في حالة استخدام واحدة ، أريد أن يتمكن جميع المطورين من استخدام مراجع التعبير في تعريفات خطواتهم. على سبيل المثال ، "عندما يقوم المستخدم $ {admin} بتسجيل الدخول إلى النظام".

الآن ، في هذه الحالة ، سيتم حل $ {admin} ربما من ملف JSON وستكون مسؤولية الحل على عاتق المطور أثناء تنفيذ تعريف الخطوة. إذا نظرت حقًا إلى هذا النوع من دقة الملكية ، فيمكن القيام بذلك عن طريق رمز عام مع عدم علم المطور به على الإطلاق.

للسماح بذلك ، يمكنني بسهولة إنشاء غلاف وظيفي عام حول عمليات تنفيذ خطوة المطور والتي ستقبل الحجج الأولية التي تم حقنها بواسطة Cucumber ، وحلها ثم حقن القيم التي تم حلها في تنفيذ الخطوة الفعلية.

حاليًا ، لن أكون قادرًا على كتابة مثل هذه الوظيفة العامة مثل التحقق من صحة Cucumber من شأنه التأكد من أن الوظيفة تحتوي على العدد الصحيح من المعلمات التي لن تكون في حالتي مثل وظيفة الغلاف العامة الخاصة بي لن تقبل أي وسيطات مسماة وسأستخدم كائن "الحجج".

آمل أن يكون لدي معنى. هناك حالات استخدام أخرى أيضًا في مشروعنا الحالي حيث ستكون هناك حاجة إلى وظيفة غلاف عامة حول تنفيذ الخطوات

إذا قمنا بتنفيذ تحويلات وسيطة الخطوة ، فهل سيؤدي ذلك إلى حل مشكلتك: https://github.com/cucumber/cucumber/wiki/Step-Argument-Transforms؟

آه نعم ، يمكن أن يعالج مشكلة القرار المرجعي. ومع ذلك ، ما زلت بحاجة إلى وضع وظيفة غلاف عامة حول تطبيقات الخطوة لأسباب أخرى.

على سبيل المثال ، أحد الأسباب هو أنني أستخدم منقلة مع الخيار كإطار عمل وفي منقلة ، يتعين على المرء أن يسجل وعودًا مخصصة في تدفق التحكم في 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 (للتأكد من أن إعادة تشغيل الخادم لا تفسد الأمور). مرة أخرى ، الكثير من الازدواجية.

ملاحظة: مساعد إعادة المحاولة:

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 التقييمات