Quantcast
Channel: Cybozu Inside Out | サイボウズエンジニアのブログ
Viewing all articles
Browse latest Browse all 689

WKWebViewでの認証ハンドリングをテストする

$
0
0

こんにちは、モバイルチームの@el_metal_です。

弊社のiOSアプリではWKWebViewでも認証が必要なコンテンツを表示しているため、WKWebViewで認証処理を行なっています。

今回はWKWebViewでの認証ハンドリングをテストする方法についてご紹介します。

WKWebViewでの認証について

主な流れはHandling an Authentication Challengeに記載されています。

WKWebViewの認証ハンドリングはWKNavigationDelegatewebView(_:didReceive:completionHandler:)が担っているので、これを呼んだ結果を検証すればテストできます。

developer.apple.com

ここでは、例えばクライアント証明書を使った通信が対象になります。

WKWebViewでのクライアント証明書を使った通信の実装に興味がある方は、こちらも併せてご覧ください。

blog.cybozu.io

前準備

webView(_:didReceive:completionHandler:)を呼ぶにあたって引数の設定が必要です。

WebView

第一引数には通信をしたいWebViewを渡します。

URLAuthenticationChallenge

第二引数はURLAuthenticationChallengeです。

developer.apple.com

このオブジェクトを新たに作る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を持つViewControllerWKNavigationDelegateに設定し、テスト対象とします。

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の認証ハンドリングは複雑になりやすい割にドキュメントに乏しく、テストを書くのに手間取りました。
本記事がお役に立てば幸いです。


Viewing all articles
Browse latest Browse all 689

Trending Articles