Cucumber-js: Opsi untuk mengendurkan argumen, periksa definisi langkah

Dibuat pada 12 Feb 2016  ·  14Komentar  ·  Sumber: cucumber/cucumber-js

Hai, Meskipun saya memahami nilai pemeriksaan ketat yang dilakukan untuk argumen langkah, saya juga merasa perlu untuk mengembalikan kontrol ini kepada pengguna.

Misalnya, dalam kasus saya, saya ingin mengimplementasikan fungsi pembungkus generik di sekitar fungsi implementasi langkah di mana saya ingin menambahkan beberapa logika pemrosesan generik sebelum dan sesudah pemanggilan langkah. Untuk pembungkus generik ini, saya perlu mengakses argumen apa pun yang diteruskan dan karenanya memerlukan akses ke array argumen daripada mendeklarasikan parameter eksplisit apa pun. Saat ini, mentimun hanya akan menghentikan saya melakukan ini karena ia melakukan pemeriksaan parameter yang ketat.

Saya ingin mengusulkan untuk menambahkan parameter konfigurasi lain ke objek opsi yang disebut mungkin sesuatu seperti skipStrictParameterCheck yang jika tidak disetel akan dianggap salah. Dengan cara itu untuk penggunaan yang paling umum, perilaku default akan menjadi pemeriksaan yang ketat tetapi untuk orang lain yang ingin menggunakan kerangka kerja untuk membangun sesuatu yang lebih di sekitarnya, ini memberi mereka fleksibilitas untuk memanfaatkan beberapa kemampuan dinamis JavaScript.

Semua 14 komentar

Saya membuat permintaan yang sama, lihat #445 :)

@riaan53 , ya saya sudah melihatnya dan sayangnya permintaan itu ditolak karena mungkin beberapa alasan yang benar. Meskipun saya tidak setuju dengan alasannya, saya berpikir bahwa sebagai bagian dari membangun kerangka kerja yang lebih besar, fitur ini penting selama pengguna tidak menyalahgunakannya, maka rekomendasi saya untuk menjadikan ini sebagai bagian konfigurasi opsional.

Apa logika pemrosesan umum yang sedang Anda bicarakan ini?

Tentu. Dalam satu kasus penggunaan, saya ingin semua pengembang dapat menggunakan referensi ekspresi dalam definisi langkah mereka. misalnya 'Ketika pengguna ${admin} masuk ke sistem'.

Sekarang, dalam hal ini ${admin} akan diselesaikan mungkin dari file JSON dan tanggung jawab untuk resolusi ada pada pengembang saat menerapkan definisi langkah. jika Anda benar-benar melihat resolusi properti semacam itu, ini dapat dilakukan dengan kode generik dengan pengembang tidak menyadarinya sama sekali.

Untuk mengizinkan ini, saya dapat dengan mudah membuat pembungkus fungsi generik di sekitar implementasi langkah pengembang yang akan menerima argumen mentah yang disuntikkan oleh Mentimun, menyelesaikannya dan kemudian menyuntikkan nilai yang diselesaikan ke dalam implementasi langkah yang sebenarnya.

Saat ini, saya tidak akan dapat menulis fungsi generik seperti validasi Mentimun akan memastikan bahwa fungsi tersebut memiliki jumlah parameter yang tepat yang dalam kasus saya tidak akan seperti fungsi pembungkus generik saya tidak akan menerima argumen bernama dan saya akan menggunakan objek 'argumen'.

Semoga saya bisa masuk akal. Ada juga kasus penggunaan lain dalam proyek kami saat ini di mana fungsi pembungkus generik di sekitar implementasi langkah akan diperlukan

Jika kami menerapkan transformasi argumen langkah, apakah itu akan menyelesaikan masalah Anda: https://github.com/cucumber/cucumber/wiki/Step-Argument-Transforms?

Ah ya, berpotensi mengatasi masalah resolusi referensi. Namun, saya masih perlu menempatkan fungsi pembungkus generik di sekitar implementasi langkah karena alasan lain.

Misalnya salah satu alasannya adalah, saya menggunakan Busur Derajat dengan Mentimun sebagai kerangka kerja dan di Busur Derajat, kita harus mendaftarkan janji-janji kustom di aliran kontrol WebDriver sehingga rajin menunggu janji-janji kustom ini untuk diselesaikan sebelum melanjutkan ke langkah berikutnya.

Sementara Mentimun tidak menunggu janji untuk diselesaikan (jika dikembalikan dari implementasi langkah), itu jelas tidak disadari oleh WebDriver dan paling sering kita perlu menambahkan kode tambahan ke setiap implementasi langkah untuk mendaftarkan janji yang dikembalikan dengan WebDriver.

Sekali lagi ini adalah hal yang sangat mudah untuk diatasi melalui pembungkus fungsi generik yang pemeriksaan parameternya harus dilonggarkan oleh Mentimun.

Saya mengalami masalah ini beberapa kali sekarang.

Yang paling menonjol adalah mengimplementasikan helper retry pada setiap langkah Then :

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

Saya menggunakan pembantu ini untuk menangani masalah waktu pada backend yang akhirnya konsisten. Pada dasarnya, ini mengeksekusi panggilan balik setiap x detik hingga y detik telah berlalu atau panggilan balik berlalu.

Sayangnya, ini menyebabkan

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

Alternatif yang saya gunakan sekarang adalah memanggil helper di setiap langkah Then , yang menyebabkan BANYAK duplikasi kode.

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

Kasus lain adalah yang lebih sederhana di mana saya ingin fungsi pembantu untuk login.

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

Juga yang ini akan menghemat banyak duplikasi kode.

Akhirnya, saya berharap di beberapa titik ingin menambahkan suite yang menjalankan semua tes penerimaan saya, tetapi me-restart server sebelum setiap Then callback (untuk memastikan server restart tidak mengacaukan segalanya). Sekali lagi, BANYAK duplikasi.

PS Pembantu coba lagi:

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

_Edit_

Mencoba meretas jalan saya:

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

tetapi

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

membuatku menjadi panda yang sedih

Berikut adalah contoh membungkus fungsi untuk mempertahankan panjang. Akan lebih baik jika hanya ada modul simpul kecil yang melakukan ini untuk Anda.

Terima kasih untuk sarannya! Itu berfungsi jika Anda menentukan jumlah argumen per definisi langkah, bukan?

misalnya.

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

Masalah yang paling mendesak bagi saya adalah tidak dapat menambahkan middleware generik untuk beberapa definisi langkah. Sesuatu seperti createProxy mungkin bisa berfungsi jika saya membuatnya menjadi objek yang memungkinkan pendaftaran middleware, dan kemudian memberi tahu jumlah argumen di setiap definisi langkah. (perhatikan baik-baik contoh pertama saya dan Anda akan melihat bahwa saya tidak dapat langsung menggunakan createProxy , karena fungsi retry akan membungkusnya. Seharusnya sebaliknya, tetapi kemudian createProxy tidak akan mengetahui jumlah argumen untuk setiap panggilan balik)

Masih terasa sangat canggung, dibandingkan dengan bisa mematikan kesalahan. :polos:

Saya pikir Anda dapat menggunakan varian dari fungsi itu di mana alih-alih meneruskan proxyLength Anda hanya meneruskan fungsi yang Anda bungkus dan gunakan function.length .

Saran yang bagus, terima kasih!

Saya mendapatkan sesuatu seperti berikut ini untuk bekerja:

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

Dua hal yang tidak diselesaikan adalah kasus pembantu login dan mengizinkan parameter default, tetapi saya dapat mengatasi keduanya.

Senang saya sekarang dapat menambahkan middleware tanpa penyesuaian definisi langkah saya!

@thomasvanlankveld Asal tahu saja, saya menemukan perpustakaan ini yang membungkus fungsi untuk memberikan panjang fungsi tertentu: https://github.com/blakeembrey/arity

@sushil-rxr dapatkah Anda melakukan sesuatu seperti di pembungkus fungsi generik Anda untuk mempertahankan panjang fungsi asli?

Di 2.0.0-rc.1 Anda sekarang dapat menambahkan pembungkus fungsi generik. Ini juga memiliki fungsi bawaan untuk mempertahankan panjang fungsi asli.

Utas ini telah dikunci secara otomatis karena tidak ada aktivitas terbaru setelah ditutup. Silakan buka edisi baru untuk bug terkait.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat