はじめに
こんにちは。会員向けアプリ「マイ ニフティ」の開発運用をしている村松です。
現在、マイ ニフティでは現在iOS・Androidアプリにユニットテストを追加しています。
その中で、iOSアプリでのAPIと通信する部分のユニットテストをサードパーティのMockライブラリなどを使わずに簡単に書くことができたので、紹介したいと思います。
AppleのWWDC18のセッションの「Testing Tips & Tricks」で紹介されている、URLProtocolを利用したMockのやり方を参考にしています。
背景
マイ ニフティ iOSアプリでは、通信処理ではAlamofireを利用しています。APIからデータ取得する部分のユニットテストを追加する際にどのようにしていくか、調査すると、以下のような手段がありました。
- Mockライブラリを使う
- DockerなどでAPIサーバーを立てる
しかし、通信処理で複雑なことはしていないため、Mockライブラリを追加するほどではなく、また、DockerなどでAPIサーバーを立てるのは管理コストやテスト実行時間の点で、採用しにくいなと感じていました。
そんな中、URLProtocolを利用する方法だと、自分たちで簡単にMockを作成できそうだったので、採用してみました。
通信処理のMockの実装
URLSessionはURLProtocolのサブクラスで実際の処理を行うため、通信処理のMockをURLProtocolのサブクラスで実装し、URLSessionで利用するようにすれば、Mockを実現することができます。
URLProtocolのサブクラスで実装すべきメソッドは以下の4つあり、startLoading で通信処理のMockを実装します。
- canInit
- canonicalRequest
- startLoading
- stopLoading
Mockの実装は求められるテスト粒度に応じて、実装していく流れになり、細かくやれば、
- HTTPメソッドが正しいか
- リクエストパラメーターが正しいか
- APIで想定されているエラーを出し分ける
などといったMockを実装することができます。
URLProtocolの実装ではURLProtocolClientのclientプロパティで、クライアント側に通信結果を伝えます。
通信成功時は
- urlProtocol(_:didReceive:cacheStoragePolicy:)
- urlProtocol(_:didLoad:)
- urlProtocolDidFinishLoading(_:)
を呼び出します。
通信失敗時は
- urlProtocol(_:didFailWithError:)
を呼び出します。
以上を踏まえて、実装したコードが以下のコードとなります。
通信処理をMockに差し替えて、テストを作成
Mock自体の実装は完了したので、次は通信処理をMockに差し替えます。
通信処理はURLSessionで行われるので、URLSessionの設定である URLSessionConfiguration でMockに差し替える設定をします。
URLSessionConfiguration には protocolClasses という配列のプロパティが存在し、このプロパティにURLProtocolのサブクラスを設定することで、Mockに処理を差し替えることができます。
テストの setUpWithError でURLSessionConfigurationの設定をして、APIと通信するテスト対象の関数にURLSessionを渡すことで、Mockを利用したテストを実現できました。
以下のコードが今回のテスト対象です。
テストコードは以下になります。
おわりに
実際に、Mockを簡単に実装できました。状況に応じて、Mockを細かく実装できるのが嬉しいですね。簡単にできるので、利用してみてください。