iOS 에서 네트워크를 테스트하는 방법은 크게 두가지가 있습니다.
1. 실제 서버(혹은 테스트 서버) 에 직접 요청하기
2. Stub 을 통해 실제 네트워크에 요청하지 않고 테스트하기
네트워크 요청은 느리고, 실패 가능성이 있으며 불확실성이 높은 요소입니다.
이런 경우에는 일정한 요청을 return 하는 Stub 을 제작하는 2번으로 주로 테스트를 진행하게 됩니다.
URLSessionDataTask vs URLSession 어떤 것을 Stubbing 할까?
테스트 코드 학습을 위해 가장 처음 접했던 포스팅은
배달의 민족 기술블로그 조원님의 "iOS Networking and Testing" 입니다.
( Moya 기반의 test code 코드 작성법도 기술되어 있으니, 꼭 읽어보시면 좋습니다. )
위에서는 MockURLSession 과 MockURLSessionDataTask 를 통해 DataTask 를 Mocking 하여
dataTask 메서드를 통해 특정한 응답을 갖는 stub 으로 변경하는 방식입니다.
하지만 iOS 13 에서는 URLSessionDataTask 이니셜라이저가 deprecated 되어 컴파일 경고가
발생하게 되었고, 추후에 나올 iOS 버전에서는 해당 방법을 적용하기 어렵다고 판단했습니다.
그 후 다른 방법을 찾기 시작했습니다.
URLSession Stubbing 하기
WWDC 18 에 소개되었던 방식입니다. URLSession 를 상속받는 MockURLSession 을 구현합니다.
이때 오버라이드해야 하는 메서드는 총 4개입니다.
1. canInit : 요청이 처리할 수 있는지 등록된 프로토콜을 확인하는 메서드 입니다. 구현에서는 true 로 고정합니다.
2. canonicalRequest : 요청을 대체할 수 있는 메서드입니다. 구현에서는 파라미터에 주어진 request 를 그대로 리턴합니다.
3. startLoading : 요청을 처리하는 메서드 입니다. 해당 메서드에서 URLProtocolClient 에 있는 메서드를 호출하여 Stub 처리를 할 수 있습니다.
4. stopLoading : 해당 메서드가 호출되면 요청 작업을 취소해야 합니다. Mock 에서는 해당 메서드는 비워둡니다.
WWDC 예제와는 조금 다르게 클로저가 아닌, Dictionary 로 Stub 을 등록하기 쉽게 구현하였습니다.
클래스 프로퍼티인 success/failureMock 를 각 유닛 테스트에 맞게 지정해주면 간단하게 이용할 수 있습니다.
startLoading 메서드 에서는 해당 Stub 이 존재하면 성공/실패 요청을 보내게 되고,
Dictionary 에 없는 값이 요청되면 MockSessionError 가 발생하게 됩니다.
Test Code 작성하기
구현한 Mock Protocol 을 사용하려면 Network 을 수행하는 클래스에서 의존성 주입으로
URLSession 을 넣어주어야 합니다. ( 방법에는 이니셜라이저, 프로퍼티, 팩토리 방식이 있습니다. )
각자의 NerworkService ( or Provider ) 는 이미 구현되어 있다고 가정해보겠습니다.
URLSessionConfiguration.ephemeral ( 로컬에 캐싱하지 않는 옵션 ) 을 통해 config 을 생성하고,
protocolClasses 를 아까 구현한 MockProtocol 로 지정해줍니다.
그 다음 urlSession 을 생성하고, 각자 의존성 주입 방식에 맞게 테스트 하려는 네트워크 객체에 주입합니다.
* Behaviour Driven Development 기반으로 테스트 코드를 작성하였습니다.
가장 핵심은 "MockURLProtocol.successMock[urlString] = Data(responseString.utf8)" 코드를 통해
네트워크 요청을 간편하게 Stub 할 수 있습니다. 그 후에는 각 프로젝트 상황에 맞게 Data 를 검증하면 됩니다.
## 필자가 더 공부가 필요한 부분
Mock 과 Stub은 Test Double 에서 나온 용어로서, 상태 검증과 행위 검증에 따라 구분한다는 것을 알게되었습니다.
다만 Apple 에서는 MockURLProtocol 로 네이밍을 했지만 개인적으로는 Stubbing 에 더 가깝다는 생각을 했습니다.
학습을 위해 다른 게시글과 자료들을 찾아보아도 Stub 과 Mock 을 혼용하는 예제들을 많이 보게 되었고,
명확하게 구분하기 어려운 개념이 아닐까 단편적으로 이해한 상태입니다.
추후에 이해도가 높아지면 내용을 추가하려 합니다.
* 현업에서는 상태와 행위 검증이 동시에 이루어지는 경우가 많기 때문에 Mock 으로 많이 사용한다는 말씀을 들었습니다.
'iOS Dev > Testing' 카테고리의 다른 글
iOS 뷰 컨트롤러 간단하게 테스트 코드 연습하기 ( with Unit Test ) (0) | 2021.09.26 |
---|---|
iOS 에서 Void Observable 테스트 코드 작성하기 (0) | 2021.07.13 |