본문 바로가기

야곰커리어캠프_TIL

20220314 / D21 / UnitTest & TDD

UnitTest

테스트를 하는것은 어떤 의미를 가질까요 ?

  • 안정성을 보장한다.
  • 유지보수에 유리하고
  • 스펙을 문서화할 수 있고
  • 코드에 자신감이 생기고
  • 깔끔한 코드 작성이 가능하다

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

이를 통해 언제라도 코드 변경으로 인해 문제가 발생할 경우, 단 시간내에 이를 파악하고 바로 잡을 수 있도록 해준다. 가짜 객체를 생성하는 것도 좋은 방법이다.
유닛테스트는 개발자뿐만아니라 보다 더 심도있는 테스트를 위해 테스터에 의해 수행되기도 한다.

유닛테스트의 이점

  • 해당 부분(메서드)에 대해서 독립적으로 테스트를 하기 때문에 빠른 리팩토링 반영과 빠른 테스트를 진행할 수 있게 된다.
  • 코드의 확장이나 리펙토링 시에도 안정성을 확보한 채로 빠르게 대응할 수 있게 된다.

작성을 위해 시간과 노력을 들여야한다는 점이 문제가 되지않는다면 유닛테스트를 작성하는 편이 이점이 많다. 왜냐면 코드가 더 잘돌아가는지 확인하기때문

통하벹스트는 단위보다 더 큰 단위의 테스트로 실제 앱동작에서처럼 객체와 객체사이의 동작에 대한 테스트가 주로 이루어진다. UITest는 UI에 대한 사용성을 테스트하는 것인데 비용이 많이 들뿐더러 실제 사용성과는 다소 차이가 있다는 이유로 유닛테스트에 비하면 많이사용되지않는다.

테스트는 어떻게 이루어지나 ?

  • 예상 값 과 결괏값을 비교하는 식으로 진행된다.

테스트가능코드에대한 WWDC-2017내용이있다.

https://developer.apple.com/videos/play/wwdc2017/414/?time=1399

FIRST원칙

  • Fast
    테스트는 빠르게 동작할 수 있어야한다. 실제 프로젝트에서는 수많은 테스트코드를 작성하게될텐데 테스트가 느리게 동작한다면 테스트를 실행하는데에만 몇분의 시간이 소요될 것이다. 테스트 코드는 빠르게 확인하고, 수정하고 반영하는데에 큰의미가 있기때문에 속도가 느린 테스트는 테스트 코드의 의미중 많은 부분을 잃어버린것과 같다.
  • Independent
    각각의 테스트는 서로 독립적이며 서로 의존해서는 안된다. 좋은단위 테스트는 최소한의 단위의 테스트에 집중할 수 있게 각테스트들이 서로에게 영향을 주어서는 안된다. 만약 다른 코드에 의존성이높거나 영향을 많이 주고 받는 경우 완전히 통제된 상황에서 테스트를 진행하기 어려울 수 있어 테스트가 실패되었을 경우 그 원인이 불분명한 경우가 발생할 수 있다.
  • Repeatable
    테스트는 언제 어디서나 같은 결과가 반복되어야한다. 즉 모든 환경을 통제하여 매번 예상한 결과대로 테스트가 진행되게 해야한다. 이를 위해서 통제가 어려운 부분에 대해서는 테스트를 위한 객체 만들어주는 방법을 선택하기도 한다.
  • Self-Validating
    테스트는 Bool을 이용하여 성공/실패에 대해서 스스로 검증이 가능해야한다. 테스트 코드 내부에서 이 테스트가 잘 동작했는지를 판별할 수 있어야한다.
  • Timely
    이상적인 테스트는 테스트하려는 실제코드를 구현하기 직전에 구현해야합니다. 만약 실제 코드를 구현한 후에 테스트 코드를 작성하면 테스트하기가 매우 까다롭거나 불가능하도록 설계가되었을지모 모릅니다.

위와같은 규칙은 꼭 지켜야하는 것은 아니다. 모두 지키기도 어렵다. 특히 마지막 Timely의 경우에 개발시간이 오래걸린다는 이유로 지켜지지않는 경우도 많다. 꼭 이런규칙을 지킨다지보다 연두해두고 테스트를 작성하는것이 더 현실적인 방향이 될 수 있을것같습니다.

테스트파일을 생성하는 방법

Xcode 파일을 접속한다.
네이게이터 목록에서 Test Naviator를 누른다
좌측 하단에 + 버튼을 눌러 유닉 테스트를 추가한다.

XCTestCase

XCTestCase 추상클래스인 XCTest의 하위클래스로 테스트를 작성하기 위해 상속해야하는 가장 기본적인 클래스이다. XCTest는 테스트를 위한 프레임워크의 이름이기도하고 테스트에서 가장 기본이 되는 추상 클래스의 이름이기도하다. XCTestCase를 상속받은 클래스에서는 test에서 사용되는 다양한 프로퍼티와 메서드를 사요할 수 있다.

setUpWithError()

setUpWithError는 각각의 test case가 실행되기 전마다 호출되어 각 테스트가 모두 같은 상태와 조건에서 실행될 수 있도록 만들어 줄 수 있는 메서드이다.
예를들어 몇몇테스트가 A라는 값에대해서 이루어지고 있는데 먼저 작성된 case에 의해서 값이 변경된다면 다음 테스트는 정상적으로 이루어지지않을 위험이 있겠지요
정상적으로 테스트를 하기위해서는 setUpWithError와 같은 메서드를 통해 test case가 이루어질 때마다 테스트의 상태를 reset시켜 주어야합니다.

tearDownWithError()

test실행이 끝난 후마다 호출되는메서드이다. 보통 setUpWithERror()에서설정한 값들을 해제할때 사용된다.

Test Life Cycle

setUpWithError -> TestCase1 -> tearDownWithError -> 반복

프로그래밍 네이밍을 한글로작성하는경우가 있다. 이유는 테스트 코드가 기능을 명세화 문서화 하는 역할도 하기때문다. 한국인끼리작업하는경우라면 한글이더 직관적이다.

@testable

UnitTest에서 실제 앱 타깃에있는 코드들에 접근하기위한 키워드이다. UnittestSample이라는 타깃에있는 코드를 작성하기위해서 필요한 코드인 것이지요. 보통앱 코드 내부에서는 internal수준의 접근 제한으로 타입을 만들어주는 것이 일반적입니다. 그렇기 때문에 앱 타킷의 타입들에 외부 타깃에서 접근하는 것이불가능한데 @testable은 테스트하는 동안에는 다른 타깃의 코드에 접근할 수 있도록 해주는 것이다.

테스트할 타입의 프로퍼티는 옵셔널 타입이 적절하다 ?

시작할때 초기화해주고 테스트를 마치고 nil로 저장을 해주고 시작하는 환경을 만들어줘야하기때문이다.

super르 호출해줘야하는이유는 상속을 받아서 사용하고 있기때문

BDD(Behavior Driven Development)라는 테스트 방식에서 가져온 것이다. BDD는 시나리오를 설정하여 예상대로 결과가 나타나는지 확인하는 방법론이다.

어떤 상황이 주어지고(given)
어떤 코드를 실행하고(when)
테스트결과를 확인하는(then)
단계로 구분하여 테스트의 흐름을 보다 쉽게 파악할 수 있다.

좀 더 이야기를 해보자면 given단계에서는 시나이로 상의 예정된 행위를 하기전에 조건등을 설정해준다.
when단계에서 예정된 행위를 하고
then단계에서 예쩡된 행위로 인해 예상한 결과를 도출하는지를 확인한다.

XCTAssertEqual로 테스트 결과를 결정하는 경우가 많음

실제로 Test함수는 XCTAssertEqual말고도 많다.

code Coverage는 위 사진과 같이 테스트의 가치를 측정해주는 툴이다. 좀 더 쉬운말로 실제 앱 코드에서 어느정도의 테스트가 진행되었을지 알 수 있는 툴이라고 할 수 있겠다

  • 실제 테스트에서 어떤 코드가 실행되었는지
  • 정확성, 성능에 대해 얼마나 충분히 테스트가 이루어졌는지
  • 테스트가 포함하고 있지않은 코드는무엇인지

@testable 어노테이션을 붙이면 private한것도 테스트할 수 있다

모든 메서드하나하나를 테스트할 필요가 있는건아니고 하나의 흐름을 검증할때도 있다.

TDD

RED -> GREEN -> REFACTOR -> 반복
실패케이스를 먼저 만들고실패한후 타입을 설계해라. -> 틀렸으니 이제 성공할수 있도록 코드를 만든다. -> 리펙토링