Unit Testing Wknavigationdelegate Functions

Unit testing WKNavigationDelegate functions swift

Your second approach of directly calling the delegate method is the better approach as you are not relying on any behavior of WebKit in your unit tests.

The problem of not being able to instantiate a WKNavigationAction can be solved by subclassing. Create a MockNavigationAction which returns the desired request you need for testing like so:

final class MockNavigationAction: WKNavigationAction {
var mockedRequest: URLRequest!
override var request: URLRequest {
return mockedRequest
}

And then in your unit test call the delegate method directly:

func test_AllowsCorrectURL() {
let sut = ViewController()
let action = MockNavigationAction()
action.mockedRequest = URLRequest(url: URL(string: "https://my-approved-url")!)
let allowExpectation = expectation(description: "Allows action")
sut.webView(WKWebView(), decidePolicyFor: action) { policy in
XCTAssertEqual(policy, .allow)
allowExpectation.fulfill()
}
waitForExpectations(timeout: 1.0)
}

Unit testing WKNavigationDelegate functions

The most straightforward way to test the delegate methods is to simply call them.

The trick here is to pass the arguments that allow the unit tests to validate the behaviour, and for this you can use test doubles.

For the particular case of the navigation policy delegate method, you can use a Fake, by subclassing WKNavigationAction, and pass an instance of that class as input argument to the delegate method:

final class FakeNavigationAction: WKNavigationAction {
let urlRequest: URLRequest

var receivedPolicy: WKNavigationActionPolicy?

override var request: URLRequest { urlRequest }

init(urlRequest: URLRequest) {
self.urlRequest = urlRequest
super.init()
}

convenience init(url: URL) {
self.init(urlRequest: URLRequest(url: url))
}

func decisionHandler(_ policy: WKNavigationActionPolicy) { self.receivedPolicy = policy }
}

Later on, in the unit test:

// setup
let testURL = URL(string: "https://my-approved-url")!
let testAction = FakeNavigationAction(url: testURL)

// act
controller.webView(webView, decidePolicyFor: testAction, decisionHandler: testAction.decisionHandler)

// assert
XCTAssertEqual(testAction.receivedPolicy, b: .cancel)

Another approach would be to swizzle the getter for request, since WKNavigationAction is an Objective-C class, however that's more of a hacky solution.

How to write unit test cases (XCTest) in UIWebView / WebKit - Swift

A good way is to create fake navigation actions to call manually the delegate.

In this question you have a good example to write test cases of this way. unit-testing-wknavigationdelegate-functions

Example to test loading in navigation:

// setup
let fakeNavigation = WKNavigation()

delegateObject.refresh() // Set loading to true and init the web view
XCTAssertTrue(delegateObject.loading)

delegateObject.webView(webView, didFinish: fakeNavigation)
XCTAssertFalse(delegateObject.loading)

Example to test the policy:

class FakeNavigationAction: WKNavigationAction {
let testRequest: URLRequest
override var request: URLRequest {
return testRequest
}

init(testRequest: URLRequest) {
self.testRequest = testRequest
super.init()
}
}

// setup
var receivedPolicy: WKNavigationActionPolicy?
let fakeAction = FakeNavigationAction(testRequest: ...)

// act
delegateObject.webView(webView, decidePolicyFor: fakeAction, decisionHandler: {
receivedPolicy = $0
})

XCTAssertEqual(receivedPolicy, theExpectedValue)

How to use didFinish navigation on an WKWebView passed to another class

Make it an instance var to retain the object so delegate to be called

var second = Second() /// << here

override func viewDidLoad(){
super.viewDidLoad()
......
}

func test(){ 
self.web!.navigationDelegate = self
self.web!.load(URLRequest(url: URL(string: "https://google.com")!))
}

How can we write Unit Test Case for the function in swift ios

Refactor the DownloadBaseViewController in your app so you can mock the dependency:

extension DownloadBaseViewController:EMPDecisionTreeCoordinatorDelegate {
// Add this variable in DownloadBaseViewController
lazy var presentingController: ViewControllerPresenting? = self.decesiontreeCoordinator?.rootViewController

func decisionEmptyTreeFeedbackButtonTapped() {
if let feedbackNavVc = storyboard?.instantiateViewController(identifier: "PremiumFeedbackNavViewController") as? PremiumCustomNavigationController {
if let feedbackVc = feedbackNavVc.children.first as? PremiumFeedbackViewController {
feedbackVc.id = self.fileDetails?.id
self.presentingController?.present(feedbackNavVc, animated: true, completion: nil)
}
}
}
}

// You need this to mock the foreign dependency on UIViewController
protocol ViewControllerPresenting: AnyObject {
func present(_ viewControllerToPresent: UIViewController,
animated flag: Bool,
completion: (() -> Void)?)
}

extension UIViewController: ViewControllerPresenting {}

In the tests you inject a Spy object that will help you validate the correct behaviour:

final class UIViewControllerSpy: ViewControllerPresenting {
var viewControllerToPresent: UIViewController!

func present(_ viewControllerToPresent: UIViewController,
animated flag: Bool,
completion: (() -> Void)? = nil) {
self.viewControllerToPresent = viewControllerToPresent
}
}

class DownloadBaseViewControllerTests: XCTestCase {

var downloadBaseViewController: DownloadBaseViewController! = DownloadBaseViewController()

func testDecisionEmptyTreeFeedbackButtonTapped() throws {
// Given
let spyController = UIViewControllerSpy()
downloadBaseViewController.presentingController = spyController
// When
downloadBaseViewController.decisionEmptyTreeFeedbackButtonTapped()
// Then
let presentedController = spyController.viewControllerToPresent as? PremiumFeedbackViewController
XCTAssertNotNil(presentedController, "Download base view controller contains feedback view controller and succesfully able to navigate")
}
}

WKNavigationDelegate methods not called

It looks like you are adding private with WKWebViewDelegate Methods, Remove those like this and it will work.

extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
debugPrint("didCommit")
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
debugPrint("didFinish")
}

func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
debugPrint("didFail")
}

}
Sample Image
Try this out, Also don't forgot to load URL in WebView or whatever you want to load.



Related Topics



Leave a reply



Submit