상세 컨텐츠

본문 제목

[Cucumber] 시나리오 Java 코드로 변환하기 (계좌 예금 및 계좌이체하기)

IT/프로그래밍

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

본문



이론으로만 공부하다보니 Cucumber와 BDD에 대해서 실제로 어떻게 테스트한다는 것인지 감이 잘 오지 않았다.

어떻게 실패하는 테스트를 짜며 테스트 코드가 어느 것인지, 테스트 케이스는 어느 것인지도 헷갈렸다.


이사님의 도움으로 시나리오를 여러개 작성한 뒤, 시나리오를 조금씩 수정해 나가며 시나리오가 TDD에서의 테스트 케이스이고, Given, When, Then을 구현하는 클래스가 테스트 코드임을 알게 되었다.


그 이후는 TDD와 똑같았다.


단지 시나리오를 테스트 형태로 바꿔서 테스트한다는것, 이것이 비즈니스 요구사항에 집중하는 것이라는것을 더 잘 알게 되었다.


아래는 실습 내용이다.


파일의 위치와 내용은 아래와 같다.




Feature파일 (테스트 케이스)


deposit.feature -> 예금 관련 시나리오

transperMoney.feature -> 계좌이체 관련 시나리오

scenarioOutline.feature -> 예금 시나리오를 scenarioOutline문법으로 표현한 시나리오


테스트 코드


DepositStepDefinitions.java ->예금 처리 테스트가 정의되어 있는 클래스

TransperStepDefinitions.java ->계좌이체 처리 테스트가 정의되어 있는 클래스


프로덕션 코드


Account.java -> 계좌

AccountTransfer.java -> 계좌이체 


그리고 마지막으로  

RunTest -> Cucumber를 이용하여 테스트를 실행하는 클래스


먼저 예금 시나리오인 deposit.feature을 보자.


Feature: Depositing money in to a User account


Scenario: 사용자 계좌에 예금하는것은 현재 사용자 잔금에 돈을 더해야됨
Given a User has 0 in their account
When 100 is deposited in to the account
Then the balance should be 100


Scenario: 200원 이상을 입금하면, 50% 이자
Given a User has 0 in their account
When 200 is deposited in to the account
Then the balance should be 300


Scenario: 300원 이상을 입금하면, 100% 이자
Given a User has 0 in their account
When 300 is deposited in to the account
Then the balance should be 600


Scenario: 잔고가 0이 아닌 경우에도 입금처리를 한다.
Given a User has 100 in their account
When 50 is deposited in to the account
Then the balance should be 150


Cucumber는 한글을 지원해준다는게 꽤 편한것 같다. (생각해보면 한글도 자연어니까 당연한거지만..)


아래의 코드는 예금 시나리오에 대한 테스트 코드이다.

public class DepositStepDefinitions {

Account account;
@Given("^a User has no money in their account$")
public void a_User_has_no_money_in_their_account() throws Throwable {
account = new Account();
}

@Given("^a User has (\\d+) in their account$")
public void a_User_has_in_their_account(int setMoney) throws Throwable {
account = new Account(setMoney);
}

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

@Then("^the balance should be (\\d+)$")
public void the_balance_should_be(int expectBalance) throws Throwable {
assertEquals(expectBalance, account.getBalance());
}
}



그리고 계좌 클래스는 아래와 같다

public class Account {

private int balance;

public Account(int setMoney) {
this.balance = setMoney;
}

public Account() {
this.balance = 0;
}

public void transferBalance(int transferValue) { //계좌이체 관련
this.balance += transferValue;
}

public void subBalance(int subValue) { //계좌이체 관련
this.balance -= subValue;
}

public void addBalance(int addMoney) { //금액에 따른 이자율 적용
if (addMoney >= 300) {
this.balance += addMoney * 2;
else if (addMoney >= 200) {
this.balance += (int) (addMoney * 1.5);
else {
this.balance += addMoney;
}
}

public int getBalance() {
return balance;
}

public void setBalance(int balance) {
this.balance = balance;
}
}


Account에 transfer과 관련된 내용이 들어가서 의아해 하는 분들도 있을 것이다.

이것은 계좌가 예금 뿐만 아니라, 송금(계좌이체)에도 쓰이기 때문이다.


계좌이체와 관련된 시나리오를 보자.


Feature: 계좌이체

Scenario: 1
Given 내 계좌에는 100만원이 있었다. 
 And 동생계좌에는 0만원이 있었다.  
When 동생계좌로 2만원을 보냈다. 
Then 내 통장에는 98만원이 남았다. 
 And 동생계좌에는 2만원이 남았다. 
 
Scenario: 2
Given 내 계좌에는 50만원이 있었다. 
 And 동생계좌에는 2만원이 있었다.
When 동생계좌로 4만원을 보냈다.
Then 내 통장에는 46만원이 남았다. 
 And 동생계좌에는 6만원이 남았다.
 
Scenario: 3
Given 내 계좌에는 5만원이 있었다. 
 And 동생계좌에는 2만원이 있었다.
When 동생계좌로 10만원을 보냈다.
Then 이체가 실패했다.


계좌이체의 테스트 코드이다.

public class TransferStepDefinitions {

AccountTransfer at = new AccountTransfer();

@Given("^내 계좌에는 (\\d+)만원이 있었다\\.$")
public void confirmBalnceInMyAccount(int initMoney) throws Throwable { Account account = new Account();
account.setBalance(initMoney);
at.setTransferAccount(account);
}

@Given("^동생계좌에는 (\\d+)만원이 있었다\\.$")
public void confirmBalnceInAnotherAccount(int initMoney) throws Throwable {
at.setTransferedAccount(new Account(initMoney));
}

@When("^동생계좌로 (\\d+)만원을 보냈다.$")
public void transferToAnotherAccount(int transferMoney) throws Throwable {
at.transfer(transferMoney);
}

@Then("^내 통장에는 (\\d+)만원이 남았다\\.$")
public void remainBalanceInMyAccount(int remainBalance) throws Throwable {
assertEquals(remainBalance, at.getTransferAccount().getBalance());
}

@Then("^동생계좌에는 (\\d+)만원이 남았다.$")
public void remainBalanceInAnotherAccount(int remainBalance)
throws Throwable {
assertEquals(remainBalance, at.getTransferedAccount().getBalance());
}

@Then("^이체가 실패했다\\.$")
public void 이체가_실패했다() throws Throwable {
}
}


그리고 계좌이체의 기능을 담당해주는 AccountTransfer클래스는 아래와 같다.

public class AccountTransfer {

private Account transferAccount;
private Account transferedAccount; 

private int balance;

public Account getTransferAccount() {
return transferAccount;
}
public void setTransferAccount(Account transferAccount) {
this.transferAccount = transferAccount;
}
public Account getTransferedAccount() {
return transferedAccount;
}
public void setTransferedAccount(Account transferedAccount) {
this.transferedAccount = transferedAccount;
}
public void setBalance(int transfer) {
this.balance += transfer;
}
public int getBalance() {
return this.balance;
}
public void transfer(int transferMoney) {
transferAccount.subBalance(transferMoney);
transferedAccount.transferBalance(transferMoney);
}
}

간단한 코드지만 내용을 굳이 이해할 필요는 없다.

Cucumber가 어떻게 메소드를 만들어주고, 어떤식으로 Java 코드를 테스트하는지 보는것이 이 포스팅의 목표이다.


처음에 계좌이체 테스트코드를 작성할때 이사님께 피드백을 받았다.

계좌를 MyAccount와 AnotherAccount 두가지로 나누어서 테스트 코드안에 각 계좌들을 나누어서 넣었다.

이것에는 2가지 문제점이 있었다.

첫번째는 나의 계좌와 상대방의 계좌를 굳이 나눌 필요가 없다는 것이다. 그냥 계좌로 묶어서 처리하고 속성값만 다르게 주면 된다.

두번째는 테스트 코드안에 비즈니스 로직이 들어가면 안된다는 것이다. 계좌를 분리하여 처리하다보니 테스트 코드에서 로직이 들어가는 부분이 있었다. 이는 테스트 코드 안에서 또 테스트 할 부분을 만드는 것이기 때문에 옳지 않다.








관련글 더보기

댓글 영역