상세 컨텐츠

본문 제목

[Cucumber] 작동원리 분석

IT/프로그래밍

by James Lee. 2015. 12. 6. 19:53

본문

테스트 코드에 RunWith(Cucumber.class)를 써놓으면, Cucumber가 이 코드를 실행한다는 의미이다.

@RunWith(Cucumber.class) //여기
@CucumberOptions(plugin = { "pretty" }, features = { "src/test" })
public class RunTest {

}
​어떤 코드를 실행하는가?
@CucumberOptions에 써있는 위치의 코드 (여기에서는 deposit.feature, RunTest.java가 될 것 같다)

Cucumber는 gherkin문법으로 feature의 내용을 다 읽어와서 구문분석을 한다.

​Given, When, Then을 식별하여 메소드로 만든다

Feature: Depositing money in to a User account
Scenario: Depositing money in to User's account should add money to the User's current balance
Given : a User has no money in their account
When : £100 is deposited in to the account
Then : the balance should be £100

이때, And가 들어가면 별개의 메소드로 뽑으라는 피드백이 들어왔다.


그런데 실행해보니 And가 들어가도 잘 인식한다. 그래서 굳이 별도의 메소드로 (Given이면 Given, When이면 When, Then이면 Then) 뽑을 필요가 있는지는 잘 모르겠다.


자, 다음으로는 Cucumber로 생성된 메소드들의 매개변수는 어떻게 받을 수 있을까?

When : £100 is deposited in to the account 의 예시를 든다

100파운드가, 계좌에 예금되었을 때 이 값은 변수로써 언제든지 값이 변할 수 있다.


이럴 때 Annotation에 정규표현식을 이용하여 아래와 같이 작성한다.

100파운드 부분에 정수를 가져오는 형태를 취하면,  £_is_deposited_in_to_the_account메소드의 매개변수는 저 정규표현식의 정수부분을 인식하여 파라미터의 전달인자로 가져오게 된다 (int amount)

@When("^£(\\d+) is deposited in to the account$")
public void £_is_deposited_in_to_the_account(int amount) {
 account.deposit(amount);
}

전달인자가 2개인 경우에는 어떻게 할까?

Then : the balance of A should be L70 And the balance of B should be $50 -> 이것을 메소드로 변환하면? (L70, $50이라는 2개의 숫자를 전달한다.)

아래와 같은 정규식을 통해 2개의 숫자를 인식하고, arg1, arg2에 순차적으로 값이 들어가게 된다. 

@Then("^: the balance of A should be L(\\d+) And the balance of B should be \\$(\\d+)$")
public void the_balance_of_A_should_be_L_And_the_balance_of_B_should_be_$(int arg1, int arg2) throws Throwable {
    throw new PendingException();
}

Annotation @에 들어간 구문은 정규표현식으로 분리하여 값을 추출한다.

값이 2개 이상 들어가 있는 경우에는 파라미터를 여러개 선언해서 순차적으로 값을 가져온다.


Given -> pre condition
When -> event
Then -> post condition


Cucumber는 두개 이상의 시나리오도 동시에 실행할 수 있다.

말 그대로, 두개 이상의 시나리오를 작성하고 실행시켜보자


Scenario: Depositing money in to User's account should add money to the User's current balance
Given : a User has no money in their account
When : £100 is deposited in to the account
Then : the balance should be £100


Scenario: Depositing money in to User's account should add money to the User's current balance
Given : a User A has no money in their account
 And a User B has $100 in his account
 And the exchange rate from $ to pound is 1.4
When : B transfer $50 to A
Then : the balance of A should be L70 And the balance of B should be $50


​결과는 아래와 같다.

2 Scenarios ( [33m2 undefined [0m)
8 Steps ( [33m8 undefined [0m)
0m0.000s


You can implement missing steps with the snippets below:

@Given("^: a User has no money in their account$")
public void a_User_has_no_money_in_their_account() throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@When("^: ?(\\d+) is deposited in to the account$")
public void ?_is_deposited_in_to_the_account(int arg1) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@Then("^: the balance should be ?(\\d+)$")
public void the_balance_should_be_?(int arg1) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@Given("^: a User A has no money in their account$")
public void a_User_A_has_no_money_in_their_account() throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@Given("^a User B has \\$(\\d+) in his account$")
public void a_User_B_has_$_in_his_account(int arg1) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@Given("^the exchange rate from \\$ to pound is (\\d+)\\.(\\d+)$")
public void the_exchange_rate_from_$_to_pound_is(int arg1, int arg2) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@When("^: B transfer \\$(\\d+) to A$")
public void b_transfer_$_to_A(int arg1) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@Then("^: the balance of A should be L(\\d+) And the balance of B should be \\$(\\d+)$")
public void the_balance_of_A_should_be_L_And_the_balance_of_B_should_be_$(int arg1, int arg2) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

 


​Given, When, Then으로 표현하는 것은 왜 중요할까?

Cucumber와 같은 parser는 굳이 Given, When, Then으로 식별을 하지 않아도 여러가지 식별방법이 많이 있을 것이다 (특수문자를 넣는다던지...)

결론부터 얘기하자면 이렇게 표현하는 것은 사용자(비즈니스 인원, 개발을 잘 모르는 사람들)입장에서 중요하다.

소스코드는 프로그래밍적 지식이 필요하지만 시나리오는 아무런 지식이 없어도 이해하는데에 지장이 없다.

BDD와 TDD의 차이점은 이전에도 말했듯이, BDD는 비즈니스 요구사항에 중점을 두고 테스트를 한다는 것이다.

따라서 문법을 모르는 비즈니스 멤버들도 테스트를 이해할 수 있어야 하므로 위와 같이 Given, When, Then으로 테스트를 진행하는 것이다.



나는 문제를 해결을 할때 너무 추상화 하려는 경향이 있다.

다시 말하여 내가 추구하는 '왜?'(Why)쪽으로 너무 치중한 나머지 '어떻게?'(How)에 대한 부분이 조금 부족할 수 있다는 것이다. Why는 물론 중요하지만 이것은 How와 조화를 이룰때 제대로 된 지식이 된다.


이제, 배운 지식을 바탕으로 BDD를 실제 코드로 구현을 해봐야겠다.

관련글 더보기

댓글 영역