Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
通信のスタブ化を自作した話 (iOS)
Search
Tatsuya Tanaka
January 14, 2017
Technology
1
1.3k
通信のスタブ化を自作した話 (iOS)
iOSでのユニットテスト用に通信をスタブ化する方法とその実装方法について。
@iOS_LT #25
Tatsuya Tanaka
January 14, 2017
Tweet
Share
More Decks by Tatsuya Tanaka
See All by Tatsuya Tanaka
iPhoneのセンサー情報をmacOSアプリでリアルタイム活用するための技術
tattn
1
690
Better use of SwiftUI
tattn
0
460
Swift Concurrencyによる安全で快適な非同期処理
tattn
2
1.4k
iOSアプリの技術選択2022
tattn
7
3.9k
Widget Suggestions 対応と ヤフーの新OS対応
tattn
1
1.3k
WidgetKitで良い体験を作るには / Good experience with WidgetKit
tattn
2
1.8k
既存アプリにSwiftUIをどう組み込んでいくか
tattn
8
2.5k
iOS 14からのアプリ内課金
tattn
5
3k
iOS 14の位置情報系アップデート
tattn
0
22k
Other Decks in Technology
See All in Technology
人工知能のための哲学塾 ニューロフィロソフィ篇 第零夜 「ニューロフィロソフィとは何か?」
miyayou
0
270
自己管理型チームと個人のセルフマネジメント 〜モチベーション編〜
kakehashi
PRO
4
650
スクラムマスターが スクラムチームに入って取り組む5つのこと - スクラムガイドには書いてないけど入った当初から取り組んでおきたい大切なこと -
scrummasudar
0
580
戰略轉變:從建構 AI 代理人到發展可擴展的技能生態系統
appleboy
0
170
Digitization部 紹介資料
sansan33
PRO
1
6.4k
AIと融ける人間の冒険
pujisi
0
110
LayerX QA Night#1
koyaman2
0
300
田舎で20年スクラム(後編):一個人が企業で長期戦アジャイルに挑む意味
chinmo
1
190
「エッジ×分散生成AI」の技術と変わる産業、そしてITの未来
piacerex
0
100
産業的変化も組織的変化も乗り越えられるチームへの成長 〜チームの変化から見出す明るい未来〜
kakehashi
PRO
0
160
TED_modeki_共創ラボ_20251203.pdf
iotcomjpadmin
0
190
[Data & AI Summit '25 Fall] AIでデータ活用を進化させる!Google Cloudで作るデータ活用の未来
kirimaru
0
4.2k
Featured
See All Featured
We Are The Robots
honzajavorek
0
130
Are puppies a ranking factor?
jonoalderson
0
2.6k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
170
Java REST API Framework Comparison - PWX 2021
mraible
34
9.1k
YesSQL, Process and Tooling at Scale
rocio
174
15k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
0
2.3k
Documentation Writing (for coders)
carmenintech
77
5.2k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.5k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.6k
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
280
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
1
330
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
0
82
Transcript
通信の スタブ化 を自作した話 Presented by Tatsuya Tanaka tattn tattn tanakasan2525
通信部分のユニットテスト 実際に通信をしてテストすることも可能。 しかし、 スタブ化することで以下のような メリット がある。 無駄なサー バー 負荷を減らせる サー
バー が開発中・ 停止中でもテストできる 任意の通信エラー をテストできる 2
通信部分のスタブ化 iOS では3 つの方法がよく用いられている。 1. 依存性注入 (DI) 2. 継承 3.
ライブラリ (OHHTTPStubs, OCMock, Mockingjay) 3
1. 依存性注入 (DI) class APIClient { let requester: Requestable init(requester:
Requestable = RealRequester()) { self.requester = requester } ... } class RealRequester: Requestable {} // 実際の通信用 class MockRequester: Requestable {} // モック用 APIClient(requester: MockRequester()) // モックに差し替え 予めDI を意識した設計が必要 4
2. 継承 open class APIClient { open func request() {}
} class MockAPIClient: APIClient { override func request() {} // モックに上書き } let apiClient: APIClient = MockAPIClient() 継承できないstruct では不可 クラスごとにモック用のクラスを作るのが面倒 5
3. ライブラリ (OHHTTPStubs, OCMock, Mockingjay) OHHTTPStubs で、 レスポンスをJSON に差し替える場合 stub(isHost("mywebservice.com"))
{ _ in let stubPath = OHPathForFile("wsresponse.json", type(of: self)) return fixture(filePath: stubPath!, headers: ["Content-Type":"application/json"]) } 使いやすい 6
今回は3 のように扱える仕組みを実装 7
その方法とは... 8
Method Swizzling & URLProtocol 9
Method Swizzling メソッドの実装を差し替える手法。 Objective-C のランタイムを使用する。 紹介時間が足りないので 詳しくはアヒルの方の記事を見てね http://qiita.com/paming/items/25eaf89e4f448ab05752 注意: Pure
なSwift のクラスでは利用できない (Swift のリフレクションは読み込み専用のため) 10
URLProtocol サポー ト外のプロトコルで通信する場合や リクエストを独自の方法で処理する時に使うクラス。 継承して独自の処理を実装。 少なくとも4 つのメソッドをoverride が必要。 class func
canInit(with request: URLRequest) -> Bool class func canonicalRequest(for request: URLRequest) -> URLRequest func startLoading() func stopLoading() 11
class func canInit(with request: URLRequest) -> Bool 引数のリクエストが処理できるか否かで真偽値を返す func startLoading()
独自のリクエストの処理を実装する func stopLoading() リクエストのキャンセル時の処理を実装する 12
スタブ化できそうな気が してきますね 13
モック用URLProtocol の実装 public class MockURLProtocol: URLProtocol { override open class
func canInit(with request: URLRequest) -> Bool { return true } override open class func canonicalRequest(for request: URLRequest) -> URLRequest { return request } override open func startLoading() { // 次のスライドで実装 } override open func stopLoading() { } } 14
モックデー タを返す override open func startLoading() { let delay: Double
= 1.0 // 通信に1 秒かかるモック DispatchQueue.main.asyncAfter(deadline: .now() + delay) { let json: [String: Any] = ["mock": "data"] let data = try! JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) self.client?.urlProtocol(self, didLoad: data!) self.client?.urlProtocolDidFinishLoading(self) } } client はURLProtocol が持っているURLProtocolClient プロトコル。 これを利用して結果を返す。 15
作ったプロトコルを使ってみる プロトコルを登録 URLProtocol.registerClass(MockURLProtocol.self) URLSessionCon guration にもプロトコルを登録 let configuration = URLSessionConfiguration.default
configuration.protocolClasses?.insert(MockURLProtocol.self, at: 0) 16
作ったプロトコルを使ってみる 作ったcon guration を利用して通信 let url = URL(string: " テストするAPI
のURL")! URLSession(configuration: configuration).dataTask(with: url) { data, _, _ in let json = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) print(json) }.resume() json にはモックデー タの["mock": "data"] が入っている。 ( 通信はしていないので" テストするAPI のURL" のまま動かしてもこの結果) 17
18
ですが 19
独自のプロトコルの作成 public class MockURLProtocol: URLProtocol { ... } プロトコルを登録 URLProtocol.registerClass(MockURLProtocol.self)
URLSessionCon guration にプロトコルを登録 let configuration = URLSessionConfiguration.default configuration.protocolClasses?.insert(MockURLProtocol.self, at: 0) con guration をURLSession に渡す URLSession(configuration: configuration) 20
面倒 21
そこで Method Swizzling 22
URLSessionCon guration の実装を置換 public extension URLSessionConfiguration { public class func
setupMock() { let `default` = class_getClassMethod(URLSessionConfiguration.self, #selector(getter: URLSessionConfiguration.default)) let swizzled = class_getClassMethod(URLSessionConfiguration.self, #selector(getter: URLSessionConfiguration.mock)) method_exchangeImplementations(`default`, swizzled) } private dynamic class var mock: URLSessionConfiguration { let configuration = self.mock configuration.protocolClasses?.insert(MockURLProtocol.self, at: 0) URLProtocol.registerClass(MockURLProtocol.self) return configuration } } 23
すると、 このようになります 24
// モックに置き換え URLSessionConfiguration.setupMock() let url = URL(string: " テストするAPI のURL")!
URLSession.shared.dataTask(with: url) { data, _, _ in let json = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) print(json) }.resume() It's シンプル ( URLSession.shared でも使えます) 25
もっと使いやすくしたい 26
ライブラリ化しました ( 宣伝) https://github.com/tattn/Mokei Method Chain でレスポンスを自由にカスタマイズ stub.url("example.com") .json(["test": "data"])
.delay(0.5) // wait for 500ms .statusCode(400) Bundle 内のJSON ファイルをスタブとして登録可能 stub.json(filename: "fixture") // fixture.json Quick/Alamo re でも使えます 27
車輪の再発明楽しい 28
参考 Alamo re, URLSession の通信処理をMethod Swizzling でスタブに置き 換える http://qiita.com/tattn/items/e7db12f84fa51b3631d2 Swift
における現実的なモック https://realm.io/jp/news/tryswift-veronica-ray-real-world-mocking- swift/ Using NSURLProtocol for Testing https://yahooeng.tumblr.com/post/141143817861/using- nsurlprotocol-for-testing 29