Cucumber-js: 단계 μ •μ˜μ— λŒ€ν•œ 인수 확인을 μ™„ν™”ν•˜λŠ” μ˜΅μ…˜

에 λ§Œλ“  2016λ…„ 02μ›” 12일  Β·  14μ½”λ©˜νŠΈ  Β·  좜처: cucumber/cucumber-js

μ•ˆλ…•ν•˜μ„Έμš”, 단계 μΈμˆ˜μ— λŒ€ν•΄ μˆ˜ν–‰λ˜λŠ” μ—„κ²©ν•œ κ²€μ‚¬μ˜ κ°€μΉ˜λ₯Ό μ΄ν•΄ν•˜μ§€λ§Œ 이 μ»¨νŠΈλ‘€μ„ μ‚¬μš©μžμ—κ²Œ λ‹€μ‹œ 전달할 ν•„μš”κ°€ μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄, 제 κ²½μš°μ—λŠ” 단계 호좜 전후에 일뢀 일반 처리 논리λ₯Ό μΆ”κ°€ν•˜λ €λŠ” 단계 κ΅¬ν˜„ κΈ°λŠ₯ μ£Όμœ„μ— 일반 래퍼 κΈ°λŠ₯을 κ΅¬ν˜„ν•˜λ €κ³  ν•©λ‹ˆλ‹€. 이 일반 래퍼의 경우 μ „λ‹¬λœ λͺ¨λ“  μΈμˆ˜μ— μ•‘μ„ΈμŠ€ν•΄μ•Ό ν•˜λ―€λ‘œ λͺ…μ‹œμ  λ§€κ°œλ³€μˆ˜λ₯Ό μ„ μ–Έν•˜λŠ” λŒ€μ‹  인수 배열에 μ•‘μ„ΈμŠ€ν•΄μ•Ό ν•©λ‹ˆλ‹€. ν˜„μž¬ μ˜€μ΄λŠ” λ§€κ°œλ³€μˆ˜λ₯Ό μ—„κ²©ν•˜κ²Œ κ²€μ‚¬ν•˜κΈ° λ•Œλ¬Έμ— 이 μž‘μ—…μ„ μˆ˜ν–‰ν•˜μ§€ λͺ»ν•˜κ²Œ ν•©λ‹ˆλ‹€.

μ„€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ false둜 κ°„μ£Όλ˜λŠ” skipStrictParameterCheck와 같은 μ˜΅μ…˜ κ°œμ²΄μ— λ‹€λ₯Έ ꡬ성 λ§€κ°œλ³€μˆ˜λ₯Ό μΆ”κ°€ν•˜λŠ” 것을 μ œμ•ˆν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€. κ·Έλ ‡κ²Œ ν•˜λ©΄ κ°€μž₯ 일반적으둜 μ‚¬μš©λ˜λŠ” κΈ°λ³Έ λ™μž‘μ€ μ—„κ²©ν•œ 검사가 λ˜μ§€λ§Œ ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‚¬μš©ν•˜μ—¬ ν”„λ ˆμž„μ›Œν¬λ₯Ό μ€‘μ‹¬μœΌλ‘œ 더 λ§Žμ€ 것을 κ΅¬μΆ•ν•˜λ €λŠ” λ‹€λ₯Έ μ‚¬μš©μžμ—κ²ŒλŠ” JavaScript의 동적 κΈ°λŠ₯ 쀑 일뢀λ₯Ό ν™œμš©ν•  수 μžˆλŠ” μœ μ—°μ„±μ„ μ œκ³΅ν•©λ‹ˆλ‹€.

λͺ¨λ“  14 λŒ“κΈ€

같은 μš”μ²­μ„ ν–ˆμŠ΅λ‹ˆλ‹€. #445λ₯Ό μ°Έμ‘°ν•˜μ„Έμš”. :)

@riaan53 , 예, λ‚˜λŠ” 그것을 λ³΄μ•˜κ³  λΆˆν–‰νžˆλ„ μ•„λ§ˆλ„ μ˜¬λ°”λ₯Έ 이유 λ•Œλ¬Έμ— μš”μ²­μ΄ κ±°λΆ€λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ‚˜λŠ” κ·Έ 좔둠에 λ™μ˜ν•˜μ§€ μ•Šμ§€λ§Œ, 더 큰 ν”„λ ˆμž„μ›Œν¬ κ΅¬μΆ•μ˜ μΌλΆ€λ‘œ 이 κΈ°λŠ₯은 μ‚¬μš©μžκ°€ 그것을 λ‚¨μš©ν•˜μ§€ μ•ŠλŠ” ν•œ μ€‘μš”ν•˜λ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€. λ”°λΌμ„œ 이것을 μ—„κ²©ν•˜κ²Œ 선택적인 κ΅¬μ„±μœΌλ‘œ λ§Œλ“œλŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

당신이 λ§ν•˜λŠ” 이 일반 처리 λ…Όλ¦¬λŠ” λ¬΄μ—‡μž…λ‹ˆκΉŒ?

ν™•μ‹ ν•˜λŠ”. ν•œ 가지 μ‚¬μš© μ‚¬λ‘€μ—μ„œλŠ” λͺ¨λ“  κ°œλ°œμžκ°€ 단계 μ •μ˜μ—μ„œ ν‘œν˜„μ‹ μ°Έμ‘°λ₯Ό μ‚¬μš©ν•  수 있기λ₯Ό λ°”λžλ‹ˆλ‹€. 예: '${admin} μ‚¬μš©μžκ°€ μ‹œμŠ€ν…œμ— λ‘œκ·ΈμΈν•  λ•Œ'.

이제 이 경우 ${admin}은 JSON νŒŒμΌμ—μ„œ 해결될 수 있으며 해결에 λŒ€ν•œ μ±…μž„μ€ 단계 μ •μ˜λ₯Ό κ΅¬ν˜„ν•˜λŠ” λ™μ•ˆ κ°œλ°œμžμ—κ²Œ μžˆμŠ΅λ‹ˆλ‹€. 그런 μ’…λ₯˜μ˜ 속성 확인을 μ‹€μ œλ‘œ 보면 κ°œλ°œμžκ°€ μ „ν˜€ μΈμ‹ν•˜μ§€ λͺ»ν•˜λŠ” 일반 μ½”λ“œλ‘œ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이λ₯Ό ν—ˆμš©ν•˜κΈ° μœ„ν•΄ Cucumberκ°€ μ£Όμž…ν•œ μ›μ‹œ 인수λ₯Ό μˆ˜λ½ν•˜κ³  ν•΄κ²°ν•œ λ‹€μŒ ν•΄κ²°λœ 값을 μ‹€μ œ 단계 κ΅¬ν˜„μ— μ£Όμž…ν•˜λŠ” 개발자 단계 κ΅¬ν˜„ μ£Όμœ„μ— 일반 ν•¨μˆ˜ 래퍼λ₯Ό μ‰½κ²Œ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

ν˜„μž¬ Cucumber의 μœ νš¨μ„± 검사λ₯Ό 톡해 ν•¨μˆ˜μ— μ˜¬λ°”λ₯Έ 수의 맀개 λ³€μˆ˜κ°€ μžˆλŠ”μ§€ ν™•μΈν•˜λŠ” 것과 같은 일반 ν•¨μˆ˜λ₯Ό μž‘μ„±ν•  수 μ—†μŠ΅λ‹ˆλ‹€. 제 κ²½μš°μ—λŠ” 일반 래퍼 ν•¨μˆ˜κ°€ λͺ…λͺ…λœ 인수λ₯Ό ν—ˆμš©ν•˜μ§€ μ•Šκ³  '인수' κ°œμ²΄μž…λ‹ˆλ‹€.

λ‚΄κ°€ 이해할 수 있기λ₯Ό λ°”λžλ‹ˆλ‹€. ν˜„μž¬ ν”„λ‘œμ νŠΈμ—λŠ” 단계 κ΅¬ν˜„μ— λŒ€ν•œ 일반 래퍼 κΈ°λŠ₯이 ν•„μš”ν•œ λ‹€λ₯Έ μ‚¬μš© 사둀도 μžˆμŠ΅λ‹ˆλ‹€.

단계 인수 λ³€ν™˜μ„ κ΅¬ν˜„ν•˜λ©΄ λ¬Έμ œκ°€ ν•΄κ²°λ©λ‹ˆλ‹€. https://github.com/cucumber/cucumber/wiki/Step-Argument-Transforms?

μ•„ 예, 잠재적으둜 μ°Έμ‘° 해상도 문제λ₯Ό ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ λ‹€λ₯Έ 이유둜 단계 κ΅¬ν˜„ μ£Όμœ„μ— 일반 래퍼 ν•¨μˆ˜λ₯Ό ​​좔가해야 ν•©λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄ ν•œ 가지 μ΄μœ λŠ” ν”„λ ˆμž„μ›Œν¬λ‘œ Cucumber와 ν•¨κ»˜ Protractorλ₯Ό μ‚¬μš©ν•˜κ³  Protractorμ—μ„œ WebDriver μ œμ–΄ 흐름에 μ‚¬μš©μž 지정 약속을 등둝해야 λ‹€μŒ λ‹¨κ³„λ‘œ μ§„ν–‰ν•˜κΈ° 전에 μ΄λŸ¬ν•œ μ‚¬μš©μž 지정 약속이 해결될 λ•ŒκΉŒμ§€ λΆ€μ§€λŸ°νžˆ κΈ°λ‹€λ €μ•Ό ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

CucumberλŠ” Promiseκ°€ 해결될 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬μ§€λ§Œ(단계 κ΅¬ν˜„μ—μ„œ λ°˜ν™˜λœ 경우) λΆ„λͺ…νžˆ WebDriverλ₯Ό μΈμ‹ν•˜μ§€ μ•ŠμœΌλ©° λ°˜ν™˜λœ Promiseλ₯Ό WebDriver에 λ“±λ‘ν•˜κΈ° μœ„ν•΄ 각 단계 κ΅¬ν˜„μ— μΆ”κ°€ μ½”λ“œλ₯Ό μΆ”κ°€ν•΄μ•Ό ν•˜λŠ” κ²½μš°κ°€ κ°€μž₯ λ§ŽμŠ΅λ‹ˆλ‹€.

이것은 λ§€κ°œλ³€μˆ˜ 검사가 Cucumber에 μ˜ν•΄ μ™„ν™”λ˜μ–΄μ•Ό ν•˜λŠ” 일반 ν•¨μˆ˜ 래퍼λ₯Ό 톡해 ν•΄κ²°ν•˜κΈ° 맀우 μ‰¬μš΄ λ¬Έμ œμž…λ‹ˆλ‹€.

λ‚˜λŠ” μ§€κΈˆμ΄ λ¬Έμ œμ— 두 번 λΆ€λ”ͺμ³€λ‹€.

κ°€μž₯ λˆˆμ— λ„λŠ” 것은 Then λ‹¨κ³„λ§ˆλ‹€ retry λ„μš°λ―Έλ₯Ό κ΅¬ν˜„ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

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

λ‚˜λŠ” 이 λ„μš°λ―Έλ₯Ό μ‚¬μš©ν•˜μ—¬ ꢁ극적으둜 μΌκ΄€λœ λ°±μ—”λ“œμ—μ„œ 타이밍 문제λ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€. 기본적으둜 yμ΄ˆκ°€ μ§€λ‚˜κ±°λ‚˜ 콜백이 μ§€λ‚˜κ°ˆ λ•ŒκΉŒμ§€ xμ΄ˆλ§ˆλ‹€ μ½œλ°±μ„ μ‹€ν–‰ν•©λ‹ˆλ‹€.

μ•ˆνƒ€κΉκ²Œλ„ 이둜 인해

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 와 같은 것은 미듀웨어 등둝을 ν—ˆμš©ν•˜λŠ” 개체둜 λ§Œλ“  λ‹€μŒ λͺ¨λ“  단일 단계 μ •μ˜μ—μ„œ 인수 수λ₯Ό μ•Œλ €μ£Όλ©΄ μž‘λ™ν•  수 μžˆμŠ΅λ‹ˆλ‹€. (제 첫 번째 예λ₯Ό μžμ„Ένžˆ μ‚΄νŽ΄λ³΄λ©΄ retry ν•¨μˆ˜κ°€ 이λ₯Ό λž˜ν•‘ν•˜κΈ° λ•Œλ¬Έμ— createProxy 직접 μ‚¬μš©ν•  수 μ—†μŒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. κ·Έ λ°˜λŒ€κ°€ λ˜μ–΄μ•Ό ν•˜μ§€λ§Œ 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

@susil-rxr μ›λž˜ ν•¨μˆ˜ 길이λ₯Ό μœ μ§€ν•˜κΈ° μœ„ν•΄ 일반 ν•¨μˆ˜ λž˜νΌμ—μ„œμ™€ 같이 ν•  수 μžˆμŠ΅λ‹ˆκΉŒ?

2.0.0-rc.1 이제 일반 ν•¨μˆ˜ 래퍼λ₯Ό μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ μ›λž˜ κΈ°λŠ₯ 길이λ₯Ό μœ μ§€ν•˜λŠ” κΈ°λŠ₯이 λ‚΄μž₯λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

이 μŠ€λ ˆλ“œλŠ” λ‹«νžŒ ν›„ 졜근 ν™œλ™μ΄ μ—†μ—ˆκΈ° λ•Œλ¬Έμ— μžλ™μœΌλ‘œ μž κ²ΌμŠ΅λ‹ˆλ‹€. κ΄€λ ¨ 버그에 λŒ€ν•œ μƒˆ 문제λ₯Ό μ—¬μ‹­μ‹œμ˜€.

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰