iOS/Testing

iOS 유닛 테스트를 배워보자!(1)

HaningYa 2020. 4. 3. 19:25
728x90

시작에 앞서

개인적으로 학과 수업을 통해 소프트웨어 개발에서 테스트의 중요성은 알고 있었지만 실제 개발에 어떻게 적용해야 하는지 모르는 상태였다. 마침 2019 GDG 부산 행사에서 DevOps 와 Testing 에 관한 세션이 있었고 그때 구체적으로 TDD 가 무엇인지 알게 되었다.

https://festa.io/events/557

 

GDG Devfest Busan 2019💖 | Festa!

Festa에서 당신이 찾는 이벤트를 만나보세요.

festa.io

Test Driven Developement란

말 그대로 테스트가 개발을 주도하는 것이다.

테스트를 먼저 만들고 그 테스트를 통과하기 위한 코드를 짜는 것을 뜻한다.

보통 개발이 끝난 후 테스트를 시행하지만 다음과 같은 장점들 때문에 그 순서를 바꾼 TDD를 적용한다.

  • 처음 해보는 프로그램 주제
  • 고객의 요구 조건이 바뀔 수 있는 프로젝트
  • 개발하는 중에 코드를 많이 바꿔야 된다고 생각하는 겨우
  • 내가 개발하고 나서 이 코드를 누가 유지보수할 지 모르는 경우
  • 즉 불확실성이 높을 경우

[출처]

 

[Agile] TDD(테스트 주도 개발)란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

[추천책]

 

테스트 주도 개발

어려움을 겪고 있는 국내의 수많은 Java 개발자들에게 도움을 주기 위해 집필된 책. 쉽고 체계적인 설명과 다양한 예제를 통해 테스트 주도 개발 방법론을 활용하고 있는 국내 개발자들의 문제를 공유하고 그 해결책...

www.aladin.co.kr

Unit Test 란

유닛 테스트(unit test)는 컴퓨터 프로그래밍에서 소스 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차다. 즉, 모든 함수와 메소드에 대한 테스트 케이스(Test case)를 작성하는 절차를 말한다. (by wiki)

[유닛 테스트를 하는 장점]

  • 문제점 발견이 쉽다
  • 리팩토링이 쉽다.
  • Integration이 간단하다
  • 유지 보수하기 편하다
  • 협업 능력이 향상된다

TDD 과 Unit test의 비교

Unit test 는 무엇을 테스팅 하느냐의 문제이고 그에 반해 TDD는 언제 테스팅을 하느냐의 문제이다.

Unit test의 경우 코드를 작성하는 때에 관계없이 할 수 있다.

그에 반해 TDD는 테스트가 개발을 주도하게 하는 것이다. 

TDD를 실천하는 방법으로 Unit test, functional test, acceptance test 가 있는 것이다.

Test Driven Development 에서 제일 중요한 것은 Driven이다.

테스트가 뭘 할지 뭐가 될지 어떤 디자인이며 어떤 API 가 될지 판단하는 것이다. 

(그렇다고 해서 테스트를 먼저 작성하고 개발한다고 TDD를 실천하는 것은 아니다. 테스트를 먼저 작성하는 건 TDD를 할 수 있는 준비만 한 셈이다.)

 

작은것 부터 천천히 해보자!

개발의 전반적인 흐름을 다루는 TDD를 배우기 전 Unit test 부터 따라해 보려고 한다.

 

[참고한 튜토리얼]

 

iOS Unit Testing and UI Testing Tutorial

Learn how to add unit tests and UI tests to your iOS apps, and how you can check on your code coverage.

www.raywenderlich.com

튜토리얼의 목표는 다음과 같다.

  • Xcode Test Navigator 를 사용해 app's model 과 asynchronous 메소드를 테스트 하는 방법
  • stub와 mock를 이용해 library 나 system 객체와의 가짜 인터랙션을 만드는 방법
  • UI와 performance를 테스트 하는 방법
  • code coverage 도구를 사용하는 방법

뭘 테스트 해야 할까

  • 핵심 : 모델 클래스와 함수 그리고 컨트롤러와의 상호작용
  • UI 워크 플로우
  • 경계값 조건
  • 고쳐진 버그

테스팅을 위한 좋은 연습 - FIRST

  • F : Fast - 테스트는 빨라야 한다
  • I : Independent/Isolated - 테스트는 state를 공유하지 않아야 한다
  • R :Repeatable - 동일한 테스트를 여러번 반복해도 같은 결과가 나와야 한다. 
  • S : Self Validating - 테스트는 자동화 되어야 한다.
    결과값은 프로그래머가 log file 분석을 통해 결정하는게 아닌  "통과" 나 "실패"여야 한다. 
  • T : Timely - 테스트는 production code를 작성하기 전에 명시되어야 한다.(TDD)

Xcode 에서의 Unit Testing

Test navigator은 테스트를 쉽게 만들어 주는 도구이다.

먼저 Test navigator 로 들어가 New Unit Test target을 만들어 준다.

만들게 되면 테스팅 프레임워크를 위한 템플릿이 만들어 진다.

import XCTest

class BullsEyeTests: XCTestCase {

    override func setUp() {
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }

    func testExample() {
        // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
    }

    func testPerformanceExample() {
        // This is an example of a performance test case.
        measure {
            // Put the code you want to measure the time of here.
        }
    }

}

여기서 3가지 방법으로 테스팅을 진행할 수 가 있다.

  1. Product -> Test 이나 Command-U를 통해 모든 테스트 클래스를 실행 한다.

  2. Test navigator 에 표시되는 재생 버튼을 클릭한다

  3. gutter 영역에 있는 다이아몬드 아이콘을 클릭한다.

 

2번 방법
3번 방법

3가지 방법으로 테스트를 진행해 보면 아직 테스트를 작성하지 않았기 때문에 빠르게 실행되는 걸 볼 수 있다.

테스트가 완료된 후 초록 체크가 생긴걸 알 수 있다.

 

모든 테스트가 끝날 경우 testPerformanceExample() 함수에 있는 마지막 회색 다이아몬트 버튼을 통해 성능 결과를 확인해 볼 수 있다.

 

XCTAssert 사용하기

core functiond을 테스트 하기 위해 XCTAssert를 사용해 보겠다.

BullsEyeGame 객체가 점수에 대한 반올림 값을 정확하게 계산하고 있는지 테스트 해보자.

 

import  선언문 아래에 해당 코드를 추가한다.

@testable import BullsEye

이코드는 unit test가 내부의 타입과 function들에 대한 접근권한을 주는 역할이다.

 

BullsEyeTests 클래스의 상단에 해당 변수를 추가한다.

var sut: BullsEyeGame!

이 코드는 BullsEyeGame 객체의 테스트와 관련된 placeholder를 제공한다.

( 이 객체는 System Under Test 또는 이 테스트 케이스 클래스가로 불려진다)

 

다음으로 setup() 함수를 수정한다.

super.setUp()
sut = BullsEyeGame()
sut.startNewGame()

이 코드는 클래스 레벨에서 BullsEyeGame 객체를 생성한다. 

이 테스트 클래스의 모든 테스트 들은 이 SUT 객체의 프로퍼티와 메소드를 통해 접근 할 수 있다.

또한 targetValue로 초기화 되는 startNewGame() 메소드도 호출 할 수 있는데 점수가 정확하게 계산되는지에 대한 대부분의 테스트 들은 이 변수를 통해서 테스트 될 것이다.

 

먼저 아래의 코드를 통해 SUT 객체를 tearDown()에 푼다.

sut = nil
super.tearDown()

 팁) SUT를 setup 메소드에서 생성하고 tearDown 메소드에서 release 하는건 모든 테스트의 시작이 초기화된 state인지 보장해줄 수 있다.

 

세팅은 끝났다. 첫번째 테스트 코드를 작성해 보자.

다음 포스팅에ㅎㅎ

728x90