안녕하세요. 크몽에서 iOS앱을 개발중인 gwangyonglee(harry)입니다.
크몽 기술 블로그에서 포스팅했었는데, 개인 블로그에서도 포스팅하려고 합니다!

이 글은 TDD를 설명하는 글이 아닌 iOS 앱 TDD 적용기에 대해 작성해보려고 합니다.

#1,2로 나누어서 작성할 예정이고, #1에서는 TDD를 적용하게 된 계기와 어떤 식으로 적용하였는지(간단한 예제 포함)에 대해 다뤄보려고 합니다.

그럼 시작하겠습니다.

TDD를 적용하게 된 계기

회사에서 프로젝트를 진행할 때 일정 산출들을 하고 계실 텐데 자신이 산출한 일정을 정확히 맞춘 적이 많으신가요?

저는 개발을 진행하면서 생각지도 못한 이슈들이 나올 것을 예상하여 하루 이틀 정도 여유롭게 잡는 편이지만, 그럼에도 불구하고 일정이 딱 맞을 경우도 있고 데드라인이 넘어가는 경우도 많았습니다.

이러한 과정이 반복되면서 프로젝트를 진행하면서 TDD를 병행하기에 일정이 많이 부담스러웠을뿐더러 TDD의 필요성을 크게 느끼지 못했습니다. TDD를 진행한다면 기술적 완성도가 올라간다는 사실은 알고 있었지만요.

그런데 프로젝트를 수행하다 보면 모바일 개발자는 기획, 디자인, 서버 작업이 어느 정도 마무리돼야 본격적으로 개발을 시작할 수 있습니다.
테크블로그용 - 프로세스1.png
STEP1,2 작업들이 완료되기까지 기획서를 먼저 보고 화면 구조 설계 및 UI틀 작업을 해봤지만 그래도 시간이 남는 경우가 있습니다.

그래서 어떻게 하면 효율적으로 균일하게 개발을 시작하고 STEP1,2가 끝났을 때 바로 개발을 들어갈 수 있을까 고민하던 도중 기획서를 보고 테스트 코드를 작성해보기 시작했습니다.

TDD 적용해보기

TDD단계는 아래 그림과 같이 이 세 단계를 거치게 됩니다.
TDD Cycle.png

RED - Failing Test Code 작성
GREEN - 기능 구현(Test Pass)
REFACTOR - 리팩토링

일단 RED 단계를 거치기 위해 [PROJECT PROCESS] 'STEP1'의 기획서를 보고 해당 기능 구현에 대해 요구사항을 정리한 후 테스트 코드(Failing Test Code)를 작성합니다.

여기서 보여드릴 간단한 예제는 현재 크몽 iOS 앱에서 전문가분들이 등록해주신 서비스 상세 화면에 있는 상단 이미지를 터치하였을 때 열리는 갤러리 뷰어에 대해서 진행하려고 합니다.


• 서비스 상세 화면 (사진 1)

테크블로그용 - 서비스 상세.jpeg

서비스 상세 화면 상단 이미지에 대한 요구사항

  1. 이미지 영역 부분 터치 시에 갤러리 뷰어가 열려야 한다.

• 갤러리 뷰어 화면 (사진 2)
테크블로그용 - 갤러리 뷰어.jpeg

테크블로그용 - 갤러리 뷰어(비디오).jpeg

갤러리 뷰어에 대한 요구사항

  1. 갤러리 뷰어에서 갤러리 개수가 2개 이상일 경우에 라벨링(1/2)으로 확인할 수 있어야 한다.
  2. 갤러리 뷰어에서 비디오 타입일 경우에 자동 재생 이후 2초 이후 플레이어 뷰가 사라져야 한다.
  3. 갤러리 뷰어 컨트롤러에서만 세로/가로 모드로 전환할 수 있어야 한다.

이제 요구사항들이 정리되었다면 실패하는 테스트 코드(RED)를 작성합니다

1번 요구사항에 대한 샘플 코드

func testMainImageView_isPresentedGalleryViewer_afterTouched() {

  // given 
  let viewController = self.viewController!
  let galleryStubData = 
  """
  {
      "type" : "IMAGE",
      "url"  : "testImage.png"
  }
  """

  let gallery = GalleryModel(JSONString: galleryStubData)

  // when
  viewController.touchUpInsideGalleryPressedClsoure?(gallery)

  // then 
  XCAssertNotNil(self.viewController.presentedViewController as? GalleryViewerController)
}

2번 요구사항에 대한 샘플 코드

func testIndicatorLabel_isShow_loadWithGallery() {

  // given 
  let pageLabel = self.viewController.pageLabel!
  var galleries: [GalleryModel] = [] 

  let galleryStubData = 
  """
  {
      "type" : "IMAGE",
      "url"  : "testImage.png"
  }
  """

  let gallery = GalleryModel(JSONString: galleryStubData)

  let galleryStubData2 = 
  """
  {
      "type" : "IMAGE",
      "url"  : "testImage2.png"
  }
  """

  let gallery2 = GalleryModel(JSONString: galleryStubData2)

  // when 
  galleries.append(gallery)
  galleries.append(gallery2)

  // then 
  XCAssertTrue(!pageLabel.isHidden)

3번 요구사항에 대한 샘플 코드

func testGalleryTypeVideo_isShowPlayerControlView_beforeVideoStart() {

  // given
  let playerControlView = self.viewController.playerControlView!

  // when 
  self.viewController.viewWillAppear(true)

  // then 
  XCAssertTrue(!playerControlView.isHidden)
}

4번 요구사항에 대한 샘플 코드

func testDeviceOrientation_isSupportOrientation_viewWillAppear() {

  // given 
  let appDelegate = UIApplication.shared.delegate as! AppDelegate 

  // when 
  self.viewController.viewWillAppear(true)

  // then 
  XCAssertTrue(appDelegate.shouldSupportAllOrientation)
}

func testDeviceOrientation_isSupportOrientation_viewWillDisappear() {

  // given 
  let appDelegate = UIApplication.shared.delegate as! AppDelegate 

  // when 
  self.viewController.viewWillDisappear(true)

  // then 
  XCAssertTrue(!appDelegate.shouldSupportAllOrientation) 
}

이렇게 실패한 테스트 코드를 작성해놓고, Step2 디자인, 서버 개발이 완료되었다면, 이제 기능 개발을 시작하면 됩니다.

기능 구현이 완료되었다면 GREEN 단계, 즉 성공하는 테스트 코드가 됩니다.

TDD를 진행하며 느낀 점

테크블로그용 - 프로세스2.png

STEP1,2가 완료되기까지 이런 식으로 테스트 코드를 작성하면, 비교적 균일하게 개발을 시작할 수 있습니다.

기획, 디자인, 서버 작업이 들어갈 때, 모바일 개발자들도 테스트 코드를 작성하고 요구사항에 해당하는 기능들을 검증합니다.

STEP2가 끝났을 때 바로 개발이 들어가는 것이 중요하다고 생각합니다.

마무리

다음 TDD(Test Driven Development) iOS 앱 적용기 #2에서는 #1에서 다루지 못했던 REFACTORING단계와 코드 커버리지를 효율적으로 확인하는 방법 등을 다루려고 합니다.

혹시 다른 부분도 포함되었으면 좋겠다 라는 부분이 있다면 댓글로 남겨주시면 적극 반영하여 포함하여 #2편을 게시하도록 하겠습니다.

많이 부족하지만 소중한 시간 내주시며 읽어주셔서 감사합니다.