【问题标题】:NSInternalInconsistencyException Completion handler passed to webView:decidePolicyForNavigationAction:decisionHandle was not calledNSInternalInconsistencyException 完成处理程序传递给 webView:decidePolicyForNavigationAction:decisionHandle 未被调用
【发布时间】:2018-07-24 22:05:39
【问题描述】:

我在我们的一项 iOS 应用测试中遇到了问题。非常感谢任何帮助解决问题!

测试代码:

    func testOpenExternalBrowser_for_valid_urls() {
    // Given
    let vc = MockDashboardViewController()
    vc.openInternalBrowserCalled = false
    vc.openExternalBrowserCalled = false
    vc.webview = WKWebView()
    vc.webview.navigationDelegate = vc

    // When
    let url = URL.init(string: "https://example.com/restOfUrl")
    XCTAssertTrue(url!.absoluteString.contains("https://example.com"), "initial url string is wrong")
    vc.webview.load(URLRequest(url: url!));
    waitSeconds(duration: 2)

    // Then
    XCTAssertFalse(vc.openExternalBrowserCalled, "openExternalBrowserCalled value is wrong")
    XCTAssertTrue(vc.openInternalBrowserCalled, "openInternalBrowserCalled value is wrong")
}

这给了我错误:

错误:-[Tests.ViewControllerTests testOpenExternalBrowser_for_valid_urls]:失败:被捕获 "NSInternalInconsistencyException", "完成处理程序传递给 -[ViewController webView:decidePolicyForNavigationAction:decisionHandler:] 不是 叫”

这是相关的 WKWebKit ViewController 代码:

    // MARK: WKNavigationDelegate methods
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    guard let url = navigationAction.request.url, let urlScheme = url.scheme, let urlHost = url.host else {
        //if we can't convert the URL, deny the action
        decisionHandler(WKNavigationActionPolicy.cancel);
        return;
    }

    var isPortValid = false;
    if let urlPort = url.port {
        isPortValid = urlPort == HostDefinitions.PORT;
    } else {
        isPortValid = urlScheme == DashboardConstants.HTTPS;
    }

    if(isPortValid && urlScheme == HostDefinitions.SCHEME && urlHost.compare(HostDefinitions.HOST) == ComparisonResult.orderedSame) {
        openInternalBrowser(url: url, decisionHandler: decisionHandler)
        return;
    }

    decisionHandler(WKNavigationActionPolicy.cancel);
    openExternalBrowser(url: url)
}

func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
    if(url.path == PathDefinitions.Login) {
        //the user has logged out or visited the login page. We will now de-auth them and direct them back to the login page
        OperationQueue.main.addOperation {
            AuthManager.logout()
            _ = self.navigationController?.popToRootViewController(animated: true);
        }
        decisionHandler(WKNavigationActionPolicy.cancel);
    } else {
        decisionHandler(WKNavigationActionPolicy.allow);
    }
}

func openExternalBrowser(url: URL) {
    AppManager.openExternalBrowser(url: url)
}

有什么想法吗?我尝试添加一个 return 语句并将决策处理程序添加到函数 openExternalBrowser 但都不起作用。应用程序正常运行(在 Safari 中打开外部 URL)但测试失败。

谢谢!

编辑: 这是 MockDashboardViewController 代码:

class MockDashboardViewController: DashboardViewController {

    var openInternalBrowserCalled = false
    var openExternalBrowserCalled = false

    var presentNotificationCalled = false

    var webviewURLString = ""

    override func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
        openInternalBrowserCalled = true
    }

    override func openExternalBrowser(url: URL) {
        openExternalBrowserCalled = true
    }

    override func presentNotification(notification: DashboardNotification, showAlertView: Bool) {
        presentNotificationCalled = true
    }
}

【问题讨论】:

  • "这是相关的 WKWebKit ViewController 代码" 但它是相关的 MockDashboardViewController 代码吗? — 另外,waitSeconds 是错误的。有一种方法可以测试异步的东西,但不是这样。确实,有人可能会争辩说您可能正在测试错误的东西。您应该嘲笑您的decidePolicyFor,而不是在真实情况下使用真实的网络视图对其进行测试;我们已经知道 web 视图可以工作,所以测试它是没有意义的。
  • 嗨@Matt;感谢您的回复!我添加了 MockDashboardViewController 代码。我只需要调用 decisionHandler(WKNavigationActionPolicy.allow);而不是在模拟控制器中?
  • 是的,或致电super。请参阅我的答案中的完整说明。

标签: swift wkwebview


【解决方案1】:

错误信息是正确的。你是说:

let vc = MockDashboardViewController()
vc.webview.navigationDelegate = vc

所以web视图的导航不是你的“相关WKWebKit ViewController”,我猜它是一个DashboardViewController,而是DashboardViewController的一个子类,即MockDashboardViewController。它说:

override func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
    openInternalBrowserCalled = true
}

好的,那么当decidePolicyFor 被调用时会发生什么? MockDashboardViewController中没有实现,所以调用了DashboardViewController的实现:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if(isPortValid && urlScheme == HostDefinitions.SCHEME && urlHost.compare(HostDefinitions.HOST) == ComparisonResult.orderedSame) {
        openInternalBrowser(url: url, decisionHandler: decisionHandler)
        return;
    }
}

这就是沿着执行路径发生的所有事情。现在,在所有这些中,decisionHandler 被称为?无处。运行时完全正确。

decisionHandler 确实会在 DashboardViewController 的 openInternalBrowser(url:decisionHandler:) 实现中被调用。这就是为什么当您运行应用程序(而不是测试)时应用程序运行良好的原因。但是在你的测试子类中,你覆盖了它——你没有调用super,所以你把这些调用扔给了decisionHandler

【讨论】:

  • 这是一个愚蠢的问题(我写的不多),但是如何从 MockDashboardViewController 中的 openInternalBrowser 函数调用 super 呢?我只想将传入的值从模拟函数返回到同名的超类 DashboardViewController 函数。但是,我最终得到“Swift 错误:源文件中的编辑器占位符”或“无法将 'URL.Type' 类型的值转换为预期的参数类型 'URL'。
  • super.openInternalBrowser(url:url, decisionHandler:decisionHandler).
  • D'oh - 交换了对象类型和名称。再次感谢您的所有帮助;是时候去看你的书了。
  • 很高兴为您提供帮助。顺便说一句,一旦您提供了所需的代码(即 MockDashboardViewController),这就会变成一个问得很好的问题。太棒了。
猜你喜欢
  • 2021-12-30
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 2021-03-18
  • 2014-08-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多