[Spock] 포팅 경험기1

페이스북에 “테스트코드와 친해지기“라는 그룹을 만들고 처음으로 한 프로젝트가

최범균님의 DDD START책에 java + junit으로 되어 있는 테스트코드를 groovy + spock으로

포팅하는 것이었습니다.

해당 프로젝트는 ddd-repeat-start 에서 확인하실 수 있습니다.

테스트코드와 친하지 않더라도 java 개발자라면 junit으로 간단한 테스트코드는 만들어 본

경험이 있을텐데요.

굳이 기존에 익숙한 junit을 쓰지 않고 spock으로 포팅하게 된 이유를 51개의 테스트케이스를

포팅하면서 정리해 보았습니다.

* groovy언어만의 장점은 해당 내용에서 제외하였습니다.


1. 의존 라이브러리 추가

spock을 사용하기 위해 기존 최범균님 프로젝트 pom.xml 파일에서 아래 라이브러리를

추가했습니다.

기존 테스트코드가 spring boot로 되어 있었기 때문에 spock-spring 모듈을 사용했습니다.

<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.0-groovy-2.4</version>
</dependency>

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.4</version>
</dependency>

cglib는 class mocking을 위해 추가했습니다. (SecurityCancelPolicyTest.groovy에 사용)

2. given-when-then 템플릿을 프레임워크 차원에서 제공

spock의 장점은 BDD(Behavior Driven Development)에서 나온 템플릿인

given(테스트할 조건을 주고)-when(테스트를 실행한 뒤)-then(결과를 검증한다) 템플릿을

프레임워크에서 제공하고 있다는 점입니다.

예전에 junit을 사용할 때는 주석을 사용해 템플릿으로 만들어 쓰고 있었지만, 이제

프레임워크에서 제공해주다 보니 자연스럽게 이 패턴대로 테스트코드를 작성할 수 있습니다.


//junit
@Test
public void addTest() {
    // given
    int a = 1;
    int b = 2;
 
    // when
    int result = a + b;

    // then
    assertThat(result, is(3));
}


//spock
def "addTest"() {
    given:
    def a = 1
    def b = 2

    when:
    def result = a + b

    then:
    result == 3
}

둘 간의 차이점이 확 느껴지시나요? spock을 사용했을 때가 훨씬 명시적이고 직관적임을

알 수 있습니다.

3. 결과검증 간편

junit을 사용할 때는 기본적으로 Junit이 제공해주는 Assert 클래스의 기능과 부가적으로

hamcrest, fest assert, assertj등의 라이브러리를 사용해서 결과를 검증해야만 했습니다.

//junit 
assertThat(result, is(1));
assertThat(result, is(true));
assertThat(result, is("test"));
assertThat(result, is(1));
assertThat(result, instanceOf(TestClass.class));
assertThat(result, nullValue());
assertThat(result, notNullValue());

@Test(expected = IllegalArgumentException.class)

try {
    a.function();
}catch(IllegalArgumentException e) {
    fail();
}
   
//spock
then:
result == 1
result == true
result == "test"
result instanceof TestClass
result == null
result != null
thrown IllegalArgumentException
notThrown IllegalArgumentException

하지만 위의 예제에서도 나오듯이 spock에서는 상대적으로 결과검증이 간편하고 직관적입니다.

4. Mock 사용 편리

junit으로 유닛테스트를 만들 때, Mock사용을 위해 mockito 라이브러리를 많이 사용합니다.

하지만 spock을 사용하면 자체적으로 Mock기능을 사용할 수 있습니다.

//junit
@Mock
private Order order;

when(order.getOrderer()).thenReturn(new MemberID("user1"), "");
 
verify(order, times(2)).purchase();

//spock
//SecurityCancelPolicyTest.groovy
given:
def order = Mock(Order)
order.getOrderer() >> new Orderer(new MemberId("user1"), "")

then:
2 * order.purchase();

stub이나 spy등을 사용할 때도, mockito에 비해 상대적으로 명시적으로 사용할 수 있습니다.

그리고 무엇보다 결과검증을 할 때, 메소드 호출의 경우 “호출횟수 * 객체.메서드명()“같이

사용할 수 있어 편리합니다.

다만, PlaceOrderServiceTest.groovy에서는 ArgumentCaptor를 사용하기 위해

mockito를 사용했습니다.

then:
ArgumentCaptor<Order> orderCaptor = ArgumentCaptor.forClass(Order.class)
verify(mockOrderRepository).save(orderCaptor.capture())

Order createdOrder = orderCaptor.getValue()
createdOrder != null

5. 동일 실행구문에서 여러 조건으로 테스트 용이

테스트코드를 만들다 보면 실행구문은 동일한데 조건을 다르게 줘야 하는 경우가 있습니다.

보통 이럴 때는 조건에 따라 변수에 값 할당이나 객체를 다르게 만들어줘야 하는데요.

spock에서는 이런 상황에서 expect(혹은 when~then)~where로 보다 편리하게

테스트코드를 만들 수 있습니다.

def "EventStore에서 이벤트를 가져온다."() {
    given:
    List<EventEntry> entries = eventStore.get(1, 2)
    
    expect:
    SampleEvent event = objectMapper.readValue(entries.get(index).getPayload(), SampleEvent.class)
    event.getName() == name
    event.getValue() == value
    
    where:
    index | name    | value
    0     | "name2" | 12
    1     | "name3" | 13
}

6. 기타

그 밖에 spock을 사용하면서 아래와 같은 장점을 느꼈습니다.

  • 메서드명에 “한글“을 넣는 것이 junit에 비해 자연스럽다
  • private 필드나 메서드 접근이 가능하다
  • 테스트 실패시 나오는 결과를 보고 원인을 쉽게 알 수 있다.

다음 포스팅에서는 spock으로 포팅하면서 겪은 어려움이나 불편함에 대해 정리할 예정입니다.

* 위 내용 중 잘못된 내용이나 수정해야 할 부분이 있으면 언제든지 의견 부탁드립니다.

[독서]2016 올해 읽은 책

기술서적


  1. 스프링 입문을 위한 자바 객체 지향의 원리와 이해
  2. 객체지향과 디자인패턴(사내스터디)
  3. 가장 빨리 만나는 Docker(초반 7장까지 읽음)
  4. 아파치 Storm을 이용한 분산 실시간 빅데이터 처리(진행중)
  5. Ansible 설정관리(진행중)
  6. 아파치 Kafka 따라잡기(진행중)
  7. 주키퍼 Zookeeper(계획)
  8. HBase in Action
  9. Gradle 철저입문
  10. 헬로 데이터 과학

 

기술자료


 

 

 

교양서적


 

 

기타


 

[2015]올해를 마치며.. 회고!

2015년도 2시간 30분 남짓 남았다.

올해도 하지 않으면 서운한,  한해를 돌아보는 의미있는 회고의 시간을 가져보자


1. 상반기 – 작년에 이은 꾸준한 도전!

이미 전 회사가 된 11번가에서 올해 상반기를 보내며, 작년정도는 아니였지만

나름 꾸준히 도전을 계속해갔다.

  • 코드리뷰 짝 매칭 알람프로그램 설계리뷰
  • 기존 코어프로젝트 패키지구조 논의
  • 프로젝트 오픈 후 QUnit 테스트코드와 클래스다이어그램 공유
  • 상품동영상제작 솔루션 연계 프로젝트 오픈 후 내용 공유
  • Nightwatch.js 소개 및 예제프로그램 시연

2. 하반기 – 새로운 도전!

새로운 도전에 대한 갈증으로 상반기 끝 무렵부터 이직준비를 하고 여러 회사에

지원하던 중, 지금의 카카오에 입사하게 되었다.

업무도메인도 기존 커머스에서 처음 해보는 메일로 바뀌었고 다시 마주한

판교까지의 길어진 출/퇴근거리만큼이나 카카오에서의 회사생활은

새로움의 연속이자 도전이었다.

3개월이 조금 넘는 짧은 시간동안 기억에 남는 건

  • 개발에만 집중할 수 있게 해주는 여러 편한 인프라 및 지원
  • 기술적 논의를 스스럼없이 할 수 있는 주변 동료 및 분위기
  • 동기부여를 일으키는 열정많고 실력 뛰어난 주변 동료들
  • Git과 Github Pull Request를 통한 코드리뷰 시도

내년엔 업무도메인 지식을 차근차근 쌓음과 동시에 새로운 시도에 주저하지 않고

내가 가지고 있는 지식과 경험들을 최대한 동료들과 공유해 나갈 계획이다

 3. 올해 처음으로 해본 새로운 도전! 인상깊은 경험!

  • 5월경부터 시작한 아침출근길 15분 걷기
  • 하루 30분정도 걷기운동시 영어듣기
  • 사내 영어회화 스터디
  • 내가 꾸준히 노력한 것에 대한 좋은 평가와 금전적 보상
  • Growing Application 세미나 참석
  • 감사한 멘토분에게 마음이 담긴 선물하기
  • 첫 맥프로, 전자책 리더기 페이퍼 구매

4. 2016년 목표

기존에 목표를 세울 때, 너무 추상적이고 실행불가능한 일들을 적어놓아 지레

겁먹고 손을 놔버렸던 설례를 되새겨 올해는 구체적으로 내가 즐기면서

하고 싶은거 위주로 생각해봤다

  • 개인 프로젝트 2개 만들기 : Bluebird, Blueluck
  • 일일코딩 도전 : 개인 프로젝트, TIL,  개인 공부 -> Github활용
  • 개인스케쥴링은 최대한 목표를 잘게 나누어 실행 -> Trello활용
  • 꾸준히 수련해야 할 것들: 객체지향, 클린코드, 리팩토링, 디자인패턴, TDD,                                                  DDD -> 블로깅 및 TIL 활용
  • 오픈소스 Contribution 도전
  • 배우고 싶은 프로그래밍 언어 : Scala, Python, Groovy, Shell script
  • 프로그래밍에 수족이 되어주는 툴을 익히자 : Intellij, Tmux, Git등
  • 영어공부도 잘게 쪼개서 계속 공부, 하루 30분 걷기운동 지속적 실천

적고보니 또 많아졌는데 나름 구체적으로 적어놨으니 실천하면서 하나씩

만들어나가면 될 것 같다.

올해 나에게 가장 큰 사건은 아무래도 “카카오 입사”가 아닐까 싶다.

회고를 마무리하며 올해도 나 자신에게 “수고했고 잘했다”라고 말해주고 싶고,

내년도 마음 한켠에 간직하고 있는 열정을 불살라 인생에 기억에 남을

사건! 하나 만들고 싶은 바램이다

 

 

[2015]중간회고!

11번가에서 일하는 동안.. 전에 회사들보다 많이 나아지긴 했지만

못내 아쉬운 것은 조금 더 사람과 업무속으로 파고 들어가야 했다는 것이다.

그렇게 했다면 지금 보다 한뼘은 더 성장해 있을것임이 확실하다

===========================================================================

2015-08-09

11번가에 오기 전, 다짐했던 것은 “환경과 사람에 휘둘리지 말고 나를 중심에 두자”였다.

하지만, 돌이켜보면 생각처럼 그게 쉽지는 않았다.

물론, 나의 가장 큰 문제였던 “생각”의 전환을 통해 전보다 훨씬 주체적으로 실행하고

도전해서 의미있는 결과를 하나씩 만들어 나갔고

그 댓가는 추후에 많은 금전적 보상과 좋은 회사로의 이직이라는 달콤한 열매를

내게 주었다.

내가 아쉬운 것은, 앞서 얘기했던 조금 더 “사람”과 “업무”에 파고들지 못한 점이다.

이 과정을 좀 더 일찍 겪었더라면 하는 아쉬움과 함께, 지금의 이 아쉬움이

내가 앞으로 한발짝 더 성장해가기 위한 시행착오의 과정이라는 생각도 든다.

적어도, 다음에는 이 같은 실수를 되풀이하지 말자라는 생각을 할 수 있기 때문이다.

내가 아무리 주체적으로 행동하고 결과를 만들어나가도 결국 우리가 하는 모든 일에는

“사람”이 엮여있음을 깨닫게 되었다.

그리고 그 “사람”과 더불어 “사람”에 엮여있는 모든 업무들속으로 더욱 깊이있게

파고드는 일종의 “집요함”도 필요할 것으로 생각한다.

예전부터 안 좋은 습관이었던 어렵거나 하기 싫은 일을 모른 척 피하거나 어물쩍

넘어가는 소위 “생까는” 버릇도 이 다음에는 반드시 고쳐야 하는 숙제이다!

그래도 지금 난 무척이나 행복하다.

11번가에서 일하는 동안, 이렇게 소중한 경험과 깨달음을 얻은 것만해도 나에게는

더할 나위 없이 큰 기쁨이기 때문이다.

그리고 지금의 이 반성은 앞으로 내가 더욱 성장해나갈 밑거름이자 자양분이

될 것임이 틀림없다.

===========================================================================

[독후감]테크니컬 리더(Becomming a Technical Leader)

BTL

최범균님의 추천으로 집어든 책이다.

사실, 읽기 전에는 제목에서처럼 “프로그래머로서의 리더는 어떤 모습일까“에 대한

정답이 있지 않을까 하는 기대감으로 책을 잡았다.

하지만, 이 책에서는 “기술“관점에서의 리더에 대한 내용은 많이 나오지 않는다.

인상깊었던 부분은,

리더십이란 사람들이 능력을 발휘할 수 있는 환경을 만들어나가는 과정이다

우리가 흔히 생각하는 리더란, 해당분야에 남들보다 더 깊은 전문성을 가진 사람으로

남다른 카리스마를 가지고 팀을 진두지휘하는 모습을 떠올리게 된다.

하지만 이 책에서 정의한 “리더십“에 대한 내용은 꽤나 신선했다.

그리고, 대부분 관리교육을 받지 못하고 개발자에서 관리자책임을 맡게 되는

경우가 많은데 이런 현실에서 내가 그 동안 겉으로 또는 속으로 비판해왔던

직책자의 모습이 곧 나의 모습일 수도 있겠다라는 생각이 이 책을 읽으면서

내내 머릿속을 떠돌았다.

400쪽에 달하는 적지 않은 분량에 그다지 흥미롭지 않은 주제이긴 했지만

조금 있으면 프로그래머 10년차에 접어들게 될 이 맘 때쯤에,

한번쯤은 내 자신을 뒤돌아보며 앞으로 나아가야 할 방향을 잡을 때

참고할만한 유용한 나침반의 역할을 이 책이 해줄 것이라 생각한다.

시간이 지나고 한번 더 차근차근 읽어도 좋을 것 같다.

[프로그램]DBUnit에서 사용하는 XML 생성기

DBUnit을 이용하여 통합테스트코드 작성시, 테스트에 쓰일 DB 테이블과 매칭되는

XML 파일이 필요하다.

이를 테스트마다 수동으로 만드는 것은 굉장히 귀찮고 비효율적인 일이어서

간단하게 Groovy를 이용하여 DBUnit용 XML을 자동으로 생성해주는 프로그램을

만들었다.

GitHub에 올린 소스는 Oracle, MySql용으로만 올렸고, 아래 소스를 수정하면

다양한 DBMS로의 응용이 가능하다.

  • 테이블 이름을 넣고 널이 아닌 컬럼을 체크하는 SQL 구문

    SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=? AND IS_NULLABLE=\’NO\’ ORDER BY ORDINAL_POSITION
  • 테이블에서 한개의 ROW를 가져오는 SQL 구문
    SELECT * FROM ‘+tableName+’ LIMIT 1

혹시, 로컬개발환경에서 Groovy 파일 실행시 한글을 인식하지 못해 문제가 생긴다면

-Dfile.encoding=UTF-8 옵션을 주면 된다.