こんにちは、モバイルチームの@el_metal_です。
弊社のiOSアプリではWKWebView
でも認証が必要なコンテンツを表示しているため、WKWebView
で認証処理を行なっています。
今回はWKWebView
での認証ハンドリングをテストする方法についてご紹介します。
WKWebViewでの認証について
主な流れはHandling an Authentication Challengeに記載されています。
WKWebView
の認証ハンドリングはWKNavigationDelegate
のwebView(_:didReceive:completionHandler:)
が担っているので、これを呼んだ結果を検証すればテストできます。
ここでは、例えばクライアント証明書を使った通信が対象になります。
WKWebView
でのクライアント証明書を使った通信の実装に興味がある方は、こちらも併せてご覧ください。
前準備
webView(_:didReceive:completionHandler:)
を呼ぶにあたって引数の設定が必要です。
WebView
第一引数には通信をしたいWebView
を渡します。
URLAuthenticationChallenge
第二引数はURLAuthenticationChallenge
です。
このオブジェクトを新たに作るinit
は一つだけです。
URLProtectionSpace
同一の認証情報が適用される範囲のことで、一般にrealmと呼ばれます。
letprotectionSpace= URLProtectionSpace(host:"cybozu.co.jp", port:0, protocol:"https", realm:nil, authenticationMethod:NSURLAuthenticationMethodClientCertificate)
URLCredential
クレデンシャル情報と、利用する場合は永続化に用いるストレージのセットです。
URLAuthenticationChallengeSender
Authentication challengeを開始したオブジェクトです。
テストでは以下のようにURLProtocol
を用意して渡すことができます。
fileprivateclassURLProtocolMock:URLProtocol, URLAuthenticationChallengeSender { // MARK: URLAuthenticationChallengeSenderfuncuse(_ credential:URLCredential, for challenge:URLAuthenticationChallenge) {} funccontinueWithoutCredential(for challenge:URLAuthenticationChallenge) {} funccancel(_ challenge:URLAuthenticationChallenge) {} // MARK: URLProtocoloverrideclassfunc canInit(with request:URLRequest) ->Bool { true } overrideopenclassfunc canInit(with task:URLSessionTask) ->Bool { true } overridefuncstartLoading() {} overridefuncstopLoading() {} openoverrideclassfunc canonicalRequest(for request:URLRequest) ->URLRequest { request } }
completionHandler
レスポンスと共に実行されるクロージャです。 テストコードでは、ここでアサーションを行うと良いでしょう。
サンプルコード
WKWebView
を持つViewController
をWKNavigationDelegate
に設定し、テスト対象とします。
finalclassViewController:UIViewController { @IBOutletprivatevarstackView:UIStackView!privatevarbrowser:WKWebView!overridefuncviewDidLoad() { super.viewDidLoad() self.browser = WKWebView(frame: .zero) self.stackView.addArrangedSubview(browser) self.browser.navigationDelegate =selfself.browser.uiDelegate =selfself.browser.load(URLRequest(url:URL(string:"https://cybozu.co.jp")!)) } } extensionViewController:WKNavigationDelegate { funcwebView(_ webView:WKWebView, didReceive challenge:URLAuthenticationChallenge, completionHandler:@escaping (URLSession.AuthChallengeDisposition, URLCredential?)->Void) { if challenge.previousFailureCount >3 { completionHandler(.performDefaultHandling, nil) return } } }
テストでは、以下のようにViewController
を作って実行します。
finalclassWKNavigationDelegateTests:XCTestCase { functest() throws { letsut= UIStoryboard(name:"ViewController", bundle:nil).instantiateInitialViewController() as!ViewController sut.view.layoutIfNeeded() URLProtocol.registerClass(URLProtocolMock.self) letwebView= sut.view.subviews .compactMap { $0as?UIStackView } .first? .subviews .compactMap { $0as?WKWebView } .first!letprotectionSpace= URLProtectionSpace(host:"example.com", port:0, protocol:"https", realm:nil, authenticationMethod:NSURLAuthenticationMethodClientCertificate) letcredential= URLCredential() letchallenge= URLAuthenticationChallenge(protectionSpace:protectionSpace, proposedCredential:credential, previousFailureCount:1, failureResponse:nil, error:nil, sender:URLProtocolMock()) sut.webView(webView!, didReceive:challenge, completionHandler: { authChallengeDisposition, _ ->Voidin XCTAssertEqual(authChallengeDisposition, .performDefaultHandling) }) } }
以上でWKWebView
の認証ハンドリングをテストすることができました。
おわりに
WKWebView
の認証ハンドリングは複雑になりやすい割にドキュメントに乏しく、テストを書くのに手間取りました。
本記事がお役に立てば幸いです。