Clean Swift에서 Test 하는 법에 대해 아라보자 (1)

Yang Si Yeon·2021년 2월 8일
1

저번에 Clean Swift를 배워본 이후, 얘를 적용해서 2개의 프로젝트를 진행했다.

첫번째 프로젝트는 Clean Swift를 적용하면서 각 컴포넌트들이 어떤 역할을 수행하는지에 대해 고민하며 개발하였고, 두번째 프로젝트는 Clean Swift를 적용한 뒤 Unit Test까지 진행해보려 한다.

Unit Test란?

아무래도 이 글을 보는 많은 사람들은 Unit Test에 대해 잘 알고 있을 것이다.
하지만 한번 더 정리하자는 의미로 Unit Test의 정의를 긁어왔다.

유닛 테스트(unit test)는 컴퓨터 프로그래밍에서 소스 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차다. 즉, 모든 함수와 메소드에 대한 테스트 케이스(Test case)를 작성하는 절차를 말한다. 이를 통해서 언제라도 코드 변경으로 인해 문제가 발생할 경우, 단시간 내에 이를 파악하고 바로 잡을 수 있도록 해준다. 이상적으로, 각 테스트 케이스는 서로 분리되어야 한다. 이를 위해 가짜 객체(Mock object)를 생성하는 것도 좋은 방법이다. 유닛 테스트는 (일반적인 테스트와 달리) 개발자(developer) 뿐만 아니라 보다 더 심도있는 테스트를 위해 테스터(tester)에 의해 수행되기도 한다.
출처: 위키백과

다시 내 말대로 정리해서 말하자면,

Unit Test란 하나의 Unit(모듈과 같은)을 테스트하기 위해 해당 모듈에서 필요로하는 input과 다른 모듈들을 가짜로 생성한 뒤 우리가 예상하는 output이 제대로 나오는지 확인하는 것이라고 볼 수 있을 것 같다. 그리고 우리는 이를 위해 Test Code를 작성한다.

Clean Swift에서의 Unit Test

Clean Swift의 구성 요소인 다양한 컴포넌트들을 모두 테스트할 수도 있지만, 해당 글에서는 가장 대표적인 VIP 컴포넌트를 테스트 하는 방법에 대해 다뤄보겠다. 얘네들을 테스트 하는 방법만 알아도 다른 애들은 금방 테스트할 수 있을 것이다.

Unit Test라는 단어를 통해 알 수 있듯이 각각 ViewController를 테스트하는 코드, Interactor를 테스트하는 코드, Presenter를 Test하는 코드를 작성해보고, 테스트를 진행 해보겠다.


Xcode에서 Unit Test 시작하기

이미 있는 프로젝트에 테스트를 추가할 수도 있고, 프로젝트를 새로 시작할 때 테스트를 포함해서 시작할 수도 있다.

테스트 준비하기

1) 프로젝트 생성할 때 같이 만들기
프로젝트를 생성할 때 Include Unit Test와 Include UI Tests 항목을 선택하면 테스트 코드의 큰 틀이 포함된 프로젝트를 만들어준다.

2) 이미 있는 프로젝트에 추가하기
navigator에서 6번째 항목으로 들어가서 하단의 + 버튼을 눌러 New Unit Test Target, New UI Test Target을 선택해서 테스트 코드의 큰 틀을 추가시킬 수 있다.

테스트 코드 작성하기

해당 글에서는 얼마전 Clean Swift를 기반으로 만든 TodoList 프로젝트를 활용해서 테스트를 진행해보도록 하겠다. 자세한 코드는 아래 링크 참고!

👉 https://github.com/ssionii/CleanSwiftTodoList

테스트 코드 작성 순서

1) 테스트 클래스 만들기
한 Unit에 대한 테스트를 진행하는 클래스는 XCTestCase를 상속받아야 한다.
예를 들어 TodoListInteractor를 테스트하는 클래스는 아래와 같이 만들 수 있다. 클래스의 이름은 어떤 컴포넌트를 테스트 하려하는지 알아보기 쉽게 지어주자.

class TodoListInteractorTest: XCTestCase {

}

2) sut
Subject under test라는 뜻으로 테스트하고자 하는 것을 뜻 한다.

예를 들어 TodoListInteractorTest라는 클래스에서는 클래스 이름을 보고 알 수 있듯이 TodoListInteractor를 테스트하게 될 것이고 코드는 아래와 같이 작성하면 된다.

var sut = TodoListInteractor()

TodoListInteractorTest 클래스의 프로퍼티로 선언하여 쓸 수 있으며, TodoList 프로젝트 안에서 만들어진 TodoListInteractor 클래스의 인스턴스를 만들기 위해선 아래와 같이 프로젝트를 import 시켜야 한다.

@testable import TodoList

3) Test LifeCycle
setUp() 함수와 tearDown() 함수를 작성한다.

애플 개발자 문서에 따르면,

  • setUp()
    테스트 케이스 1개가 시작되기 전에 한번 실행되는 함수로 테스트 케이스가 실행되기 전 특정 상태를 지정하려면 해당 함수를 재정의 하면 된다. 예를 들어 객체의 인스턴스를 만들거나, DB를 초기화하는 등의 일을 setUp 함수에서 하면 좋다.

  • tearDown()
    테스트 케이스 1개가 끝나면 한번 실행되는 함수로 모든 테스트 함수가 종료된 후 정리를 할 때 해당 함수를 재정의 하면 된다. 예를 들어 파일을 닫거나, 메모리를 해제하는 등의 일을 tearDown 함수에서 하면 좋다.

setUp()과 tearDown() 함수에서는 테스트에 직접적으로 관련되진 않지만, 테스트를 수행할 때 꼭 필요한 일들을 수행한다. 따라서 테스트 함수를 1개의 항목에 대해서만 테스트를 진행할 수 있도록 깔끔하게 작성할 수 있다.

예를 들어 sut을 초기화하는 함수는 setUp() 함수에서 호출 할 수 있다.

// Subject under test
var sut: TodoListInteractor!

// Test lifecycle
override func setUp(){
	super.setUp()
	setupTodoListInteractor()
}

override func tearDown() {
	super.tearDown()
}

// Test setup
func setupTodoListInteractor(){
	sut = TodoListInteractor()
}

4) test doubles
test double 이란 실제 객체를 대신해서 테스팅에서 사용하는 모든 방법을 말한다.

예를 들어, Clean Swift에서는 Interactor가 제대로 동작하는지 확인하기 위해

  • Interactor에게 response를 전달하는 Worker
  • response를 받는 Presenter

이 2가지가 필요하다.

TodoListInteractor를 테스트하기 위해 우리가 만든 TodoListPresenter를 사용할 수 없다. Unit Test는 컴포넌트를 하나하나 떼어내서 해당 컴포넌트에서 필요로 하는 input과 다른 것들을 가짜로 생성한 뒤 예상하는 output이 제대로 나오는지 확인하는 방법으로 수행해야 한다. 따라서 이 가짜 Worker, Presenter 객체들(test doubles)을 만들어 주어야 한다.

테스트는 Independent 해야 한다.

test double 의 종류는 총 5가지가 있는데, 용도가 무엇이냐에 따라 이름이 다르다.

  • Dummy : 객체는 전달되지만 사용되지 않는 객체
  • Stub : 테스트에서 호출된 요청에 대해 미리 준비해둔 결과를 제공함
  • Fake : 동작하는 구현을 가지고 있지만 실제 프로덕션에는 적합하지 않음
  • Spy : Stub의 역할을 하면서 호출된 내용에 따라 약간의 정보를 기록
  • Mock : 호출에 대한 기대를 명세하고, 해당 내용에 따라 동작하도록 프로그래밍된 객체

이렇게 글로만 보면 뭔소린지 모를 것이다. 이것에 대한 정리는 추후에 하는 걸로,,,

일단 이번 글에서는 Interactor의 어떤 함수가 호출 될 때 Presenter의 어떤 함수를 잘 호출 했는지를 확인하는 용도로 Spy를 사용하도록 하겠다.


5) test
자 이제 테스트 메소드를 작성해보자.

테스트 케이스는 명세 기반 테스트의 설계 산출물로, 설계된 입력값, 실행조건, 기대 결과로 구성된 테스트 항목의 명세서를 말한다.
테스트 메소드는 해당 테스트 케이스의 입력 값과 실행 조건, 기대 결과를 실행하고 확인하는 역할을 한다.

테스트 메소드는 메소드명 앞에 test 접두어를 붙어야 한다. 테스트 메소드 왼쪽에는 다이아몬트 모양의 버튼이 생기고 해당 버튼을 누르면 해당 메소드만 실행할 수 있다. (즉, 테스트 메소드 별로 실행 가능하다.)

또한, 테스트 케이스는 분리해서 작성해야한다. 예를들어 하나의 테스트케이스에서 nil 값도 확인하고, 함수가 잘 불렸는지도 확인하면 안된다. 따라서 각 메소드는 하나의 기능만 수행해야한다.




설명을 바탕으로 View Controller, Interactor, Presenter를 테스트 하는 예시까지 함께 올리고 싶었는데, 글이 너무 길어졌다. 2편으로 나누어서 포스팅을 하도록 하겠다.


참고

https://medium.com/short-swift-stories/how-to-write-unit-tests-in-clean-swift-4d297bcc559d

https://brunch.co.kr/@tilltue/55

profile
가장 젊은 지금, 내가 성장하는 데에 쓰자

1개의 댓글

comment-user-thumbnail
2021년 7월 20일

좋은 글 감사드립니다^^

답글 달기