Xcode 6, Swift and Mock Verification Without Exceptions

OCMock: Why do I get an unrecognized selector exception when attempting to call a UIWebView mock?

This turned out to be one of those off by one character issues that you don't notice until you've looked at it a few dozen times.

Per this post on the OCMock forums, I had set my Other Linker Flags for my unit test target to -ObjC -forceload $(PROJECT_DIR)/Libraries/libOCMock.a. This is wrong; -forceload should have been -force_load. Once I fixed this typo, my tests worked.

Swift XCTest: Verify proper deallocation of weak variables

The problem is that your testWeakVarDeallocation() function hasn't exited when the dispatchAsync block is called so a strong reference to strongMock is still held.

Try it like this (allowing testWeakVarDeallocation() to exit) and you'll see weakMock becomes nil as expected:

class weakTestTests: XCTestCase {
var strongMock: Mock? = Mock()

func testWeakVarDeallocation() {
weak var weakMock = strongMock

print("weakMock is \(weakMock)")

let expt = self.expectation(description: "deallocated")

strongMock = nil

print("weakMock is now \(weakMock)")

DispatchQueue.main.async {
XCTAssertNil(weakMock) // This assertion fails

print("fulfilling expectation")
expt.fulfill()
}

print("waiting for expectation")
self.waitForExpectations(timeout: 1.0, handler: nil)
print("expectation fulfilled")
}
}

How can I verify a class method is called using XCTAssert?

Create a property on your mock, mutating it's value within the method you expect to call. You can then use your XCTAssertEqual to check that prop has been updated.

   func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

class OAuthServiceMock: OAuthService {
var renderOAuthWebViewExpectation: XCTestExpectation!
var didCallRenderOAuthWebView = false

override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
didCallRenderOAuthWebView = true
renderOAuthWebViewExpectation.fulfill()
}
}

let sut = OAuthServiceMock(apiClient: mockAPIClient)

XCTAssertEqual(sut.didCallRenderOAuthWebView, false)
sut.renderOAuthWebViewExpectation = renderOAuthWebViewExpectation

sut.initAuthCodeFlow()
waitForExpectations(timeout: 1) { _ in
XCTAssertEqual(sut.didCallRenderOAuthWebView, true)
}

}

How to write do {} catch {} in Xcode unit test with 100% code coverage

As has been mentioned in the comments, you should expect your unit test code to not have full coverage; especially for the XCTFail calls. The whole goal of the unit test is to never hit that line.

Even if you restructured your source to bring the XCTFail somewhere else, you're still intending to have it never executed. You could accomplish more code coverage by using XCTAssertEqual again.

func testUrlRequest_WithAuthenticationNoToken_ExpectingAuthenticationFailure() {
let mockController = MockAuthenticationController()
mockController.token = nil
Server.authenticationController = mockController
var failed = false
do {
_ = try Server.urlRequestWithHeaders(to: arbitraryEndpoint, excludeBearerToken: false)
} catch {
XCTAssertEqual(error as? Server.Errors, .authenticationFailure)
failed = true
}
XCTAssertEqual(failed, true, "Expected throw when no token is present")
}

Can you do test setup before application loads in Swift?

One way to do it would be to have an separate NSApplicationMain for when unit tests are run vs one for "normal" runs.

First remove the @NSApplicationMain annotation from your current AppDelegate class. It should end up looking something like this:

AppDelegate.swift

import AppKit

class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
print("Debug/Production run")

// Insert code here to initialize your application
}

func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}

Now create a new file called AppDelegateUnitTesting.swift and it's source should look like this:

AppDelegateUnitTesting.swift

import Foundation
import Cocoa

class AppDelegateTesting: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
print("Unit Testing Run")
// Insert code here to initialize your application
}

func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}

Now add a new file called main.swift this file will determine in which environment our app is running, the source should be something like this:

main.swift

import Foundation
import Cocoa

let isRunningTests = NSClassFromString("XCTestCase") != nil &&
ProcessInfo.processInfo.arguments.contains("-XCUnitTests")

fileprivate var delegate: NSApplicationDelegate?

if !isRunningTests {
delegate = AppDelegate()
NSApplication.shared.delegate = delegate

// See this Answer to initialize the Windows programmatically
// https://stackoverflow.com/a/44604229/496351
} else {
delegate = AppDelegateTesting()
NSApplication.shared.delegate = delegate

}
NSApplication.shared.run()

To determine whether it's running in a Unit Test environment it checks if it can load the XCTestClass (which is only injected when testing) and it checks for the presence of the -XCUnitTest command line argument, we have to set this argument ourselves as part of the Scheme's Test action as shown in the image below

Test Args

After doing all of this, you should see the message "Debug/Production run" printed when you press the play button and you should see the message "Unit Testing Run" printed whenever you run your unit tests.

You'll most likely have to add code to load the initial window programmatically this other answer shows how to do it:

  • how to Load initial window controller from storyboard?

How to unit test throwing functions in Swift?

EDIT: Updated the code for Swift 4.1 (still valid with Swift 5.2)

Here's the latest Swift version of Fyodor Volchyok's answer who used XCTAssertThrowsError:

    enum MyError: Error {
case someExpectedError
case someUnexpectedError
}

func functionThatThrows() throws {
throw MyError.someExpectedError
}

func testFunctionThatThrows() {
XCTAssertThrowsError(try functionThatThrows()) { error in
XCTAssertEqual(error as! MyError, MyError.someExpectedError)
}
}

If your Error enum has associated values, you can either have your Error enum conform to Equatable, or use the if case statement:

    enum MyError: Error, Equatable {
case someExpectedError
case someUnexpectedError
case associatedValueError(value: Int)
}

func functionThatThrows() throws {
throw MyError.associatedValueError(value: 10)
}

// Equatable pattern: simplest solution if you have a simple associated value that can be tested inside 1 XCTAssertEqual
func testFunctionThatThrows() {
XCTAssertThrowsError(try functionThatThrows()) { error in
XCTAssertEqual(error as! MyError, MyError.associatedValueError(value: 10))
}
}

// if case pattern: useful if you have one or more associated values more or less complex (struct, classes...)
func testFunctionThatThrows() {
XCTAssertThrowsError(try functionThatThrows()) { error in
guard case MyError.associatedValueError(let value) = error else {
return XCTFail()
}

XCTAssertEqual(value, 10)
// if you have several values or if they require more complex tests, you can do it here
}
}


Related Topics



Leave a reply



Submit