I'm writing a test method where I want the SUT to throw an exception when under certain conditions. The code looks like this:
- (void) testCantStartTwice
{
XCTAssertThrows([self.sut start], #"");
}
Now, all is good and the test passes. However, I have Xcode set an Exception Breakpoint for all ObjC exceptions, which is pretty useful when testing out an app in the debugger. As you now, now when I execute my test suite with ⌘U, now it stops at that test and looks like if it's failing, even though it says "Test Succeeded".
Any way of making the breakpoint not stop at that test?
Thanks and all the best
Unfortunately, it does not seem possible to have exception breakpoints ignore anything wrapped in XCTAssertThrows (or now XCTAssertThrowsError).
Workaround 1: Deal with the breakpoints or script around them
When you execute these tests, you can either do the following:
Disable the exception breakpoint.
Hit run every time it hits one of your breakpoints.
Both are not ideal as you'll be doing some manual work.
There are suggestions for how to disable breakpoints for Swift XCTAssertThrowsError assertions here. I have not tested those.
Workaround 2: Return Result<T, Error> instead
My original method looked like this:
struct Truck {
static func makeTruck(numberOfWheels: Int) throws -> Self {
if numberOfWheels < 4 {
throw TruckError.tooFewWheels
}
// ...
return .init()
}
}
// Code example:
let truck: Truck? = try? Truck.makeTruck(numberOfWheels: 1)
// Test example:
XCTAssertThrowsError(try Truck.makeTruck(numberOfWheels: 1))
To work around this problem of breakpoints, I created a duplicate internal function that is more testable (since it won't cause the breakpoint problem) by returning a Result instead of throwing an Error.
struct Truck {
static func _makeTruck(numberOfWheels: Int) -> Result<Truck, Error> {
if numberOfWheels < 4 {
return .failure(TruckError.tooFewWheels)
}
// ...
return .success(.init())
}
static func makeTruck(numberOfWheels: Int) throws -> Self {
switch _makeTruck(numberOfWheels: numberOfWheels) {
case .failure(let error):
throw error
case .success(let value):
return value
}
}
}
// Code example:
let truck: Truck? = try? Truck.makeTruck(numberOfWheels: 1)
// Test example:
XCTAssertEquals(Truck._makeTruck(numberOfWheels: 1), .error(TruckError.tooFewWheels))
Workaround 3: Return T? instead
Finally, if you have a simpler use-case, and your function's failure is quite obvious (e.g. there is only one type of error it can return), consider just returning an optional instead of needing the complexity of the Result type:
struct Truck {
static func makeTruck(numberOfWheels: Int) -> Truck? {
if numberOfWheels < 4 {
return nil
}
// ...
return .init()
}
}
// Code example:
let truck: Truck? = Truck.makeTruck(numberOfWheels: 1)
// Test example:
XCTAssertNil(Truck.makeTruck(numberOfWheels: 1))
Perhaps you could use a Test Failure Breakpoint instead of an Exception breakpoint when you are testing. The wwdc 2013 video on unit testing outlined a pretty good workflow for inspecting test failures. It essentially said:
Set a Test Failure Breakpoint which will allow you to inspect the conditions that caused the failure.
If needed, set a manual breakpoint earlier in the test and rerun so you can step through the statements leading to the failure as usual.
This isn't really a direct answer, but as far as I know, I don't think there is a way to create exceptions to the exception breakpoint. Hope it helps
I had the same issue and I was looking for solution for 2 hours. Probably we can't do anything with that.
Related
I'm relatively new to Swift, so I hope I'm not asking a stupid question.
I have some code that instantiates an array of type Error, which will later be iterated and printed to the console. When running this code through Instruments using the "Leaks" instrument, it shows a leak of _SwiftNativeNSError. If I change the array type from [Error] to [Any], the leak disappears, even though it is still actually holding an object conforming to Error. The leak is not reproducible with any other data types or protocols that I've tried.
Here's some sample code:
class myLeak {
lazy var errors = [Error]()
enum err: Error {
case myFirstError
}
func doSomething() {
errors.append(err.myFirstError)
for error in errors {
print(String(describing: error))
}
}
}
// call with let myleak = myLeak(); myleak.doSomething()
Calling the doSomething() function immediately creates a leak. Switching [Error]() to [Any]() resolves the leak, but I'm not happy with this as a solution without understanding the underlying problem. The problem is also solved by changing [Error]() to my enum implementing the Error protocol: [err](). I've also tried creating my own custom protocol just to prove if this is being caused specifically by Error, and I'm only able to reproduce the problem when using Error; my own custom protocol did not exhibit this behaviour.
Originally, my code used a forEach loop to iterate the array, but I then tried re-writing it to use a standard for loop in case the closure in forEach was causing the issue, but this didn't work.
I'm suspicious that this may be a Swift bug (in which case, I will open an issue for it), but there's also a chance that I'm missing a key piece of understanding. If what I'm doing is bad practice, I'd like to understand why.
Update:
After speaking with Joe Groff, an Apple engineer, this is the bug you could have encountered: https://bugs.swift.org/browse/SR-6536
Original Answer
I've played a bit with your code and I think the problem is due to Error type.
In fact, taking the code by Josh, you can find a different behaviour if you use Error or MyError as the type of your array.
I guess the problem arises since the deinit call is not forwarded to CustomObject since Error is just a protocol and it's not aware of the underlying class. While, MyError is. We can wait for other people to have clarifications on this behaviour.
Just for simplicity, I'm using a Playground here. See that I'm not even trying to print the error value.
import UIKit
class ViewController: UIViewController {
var errors: [Error] = [] // change to MyError to see it working
enum MyError: Error {
case test (CustomObject)
}
class CustomObject {
deinit {
print("deiniting")
}
}
override func viewDidLoad() {
super.viewDidLoad()
let testerror = MyError.test(CustomObject())
errors.append(testerror)
errors.removeAll()
}
}
do {
let viewController = ViewController()
// just for test purposes ;)
viewController.viewDidLoad()
}
I tested your code and it looks like the String(describing) statement is causing the string to retain the error, which is just weird. Here is how I can tell: I created an associated object that prints out when its being deinitialized.
import UIKit
class ViewController: UIViewController {
var errors = [Error]()
override func viewDidLoad() {
super.viewDidLoad()
class CustomObject {
deinit {
print("deiniting")
}
}
enum MyError: Error {
case test (CustomObject)
}
let testerror = MyError.test(CustomObject())
errors.append(testerror)
for error in errors {
//print(String(describing: error))
}
errors.removeAll()
}
}
When the print doesn't run, sure enought he associated object is deinitialized at when the error is removed from the array and the output is:
deiniting
Uncomment the print and the output becomes:
test(CustomObject #1 in stack.ViewController.viewDidLoad() -> ())
At first I thought it was the print that's the problem but If I refactor to:
errors.forEach{print($0)}
I get the output:
test(CustomObject #1 in stack.ViewController.viewDidLoad() -> ())
deiniting
But if I change it to:
errors.map {String(describing:$0)}.forEach{print($0)}
Then deinit is no longer called:
test(CustomObject #1 in stack.ViewController.viewDidLoad() -> ())
Weirdness. Maybe file a radar?
This bug was Fixed in Xcode 9.3.
We have an app with a fairly broad Core Data model, with lots of custom subclasses implemented in Objective C, but which are also used by a growing fraction of the app that's written in Swift. (For what it's worth: we're building with Xcode 7.3.1 against iOS 9.3, and the Swift code is thus 2.2.)
There's a helper function written in Swift that looks like this:
extension NSManagedObject {
func inContext<T: NSManagedObject>(moc: NSManagedObjectContext) -> T? {
guard self.managedObjectContext != moc else {
return (self as! T)
}
do {
let obj = try moc.existingObjectWithID(self.objectID)
return (obj as! T) // <--- fails here
}
catch let error {
return nil
}
}
}
This is called in a fair number of places where objects jump contexts. Calling code generally looks like this:
let result: ECFoo? = foo.inContext(managedObjectContext)
This works flawlessly in debug builds of the app. But with optimizations turned on, I'm running into a case where this call fails at the line I've marked, where the fetched object is being cast from NSManagedObject to the correct subclass. The stack trace starts with swift_dynamicCastObjCClassUnconditional, and the message that gets logged to the console is:
Could not cast value of type 'ECFoo_Foo_' (0x7fb857d2c250) to 'ECFoo_Foo_' (0x7fb857d2c250).
If I put a breakpoint on that line, what I'm attempting to do seems fine in the debugger console:
(lldb) po moc.existingObjectWithID(self.objectID) is ECFoo
true
This is deeply confusing, because it's clearly the same type on both sides here, and they both appear to be the dynamically generated subclass, rather than the formal class that it should be trying to cast to (based on inference of the calling code). I can only assume that there's some piece of information being optimized away that is necessary to make this work, but I'm not entirely sure how to fix it.
In the following code, I want to test whether "DisplayHelper.displayAlert" has been called. I am dependency-injecting DisplayHelper and AuthService mock objects, and using PromiseKit
In My ViewController:
#IBAction func submitButtonPressed(sender: AnyObject) {
AuthService.updateUser(["zipcode": self.zipcodeTextField.text!])
.then { user -> Void in
// sucess code
}.error { error -> Void in
self.DisplayHelper.displayAlert("Zipcode Not Found", message: "We can't find that zipcode... please try again.", callingViewController: self)
}
}
Here are the not-working tests:
func testSubmitButtonServerCantFindZipcode() {
vc.zipcodeTextField.text = "00000"
vc.submitButtonPressed(self)
// called AuthService (passes)
XCTAssertEqual(authServiceMock.updateUserCalled, true)
// called displayAlert (fails because tests run before DisplayAlert is called)
XCTAssertEqual(displayHelperMock.displayAlertCalled, true)
}
How do I get the tests to wait for all of the code to execute before the assertions?
When testing asynchronous code with XCTest you need to use XCTestExpectation.
You can rewrite the test code like this:
let e = expectationWithDescription("display alert")
waitForExpectationsWithTimeout(3) { error in
XCTAssertEqual(displayHelperMock.displayAlertCalled, true)
}
Now the only missing piece to make the test work is finding a place where to call expectation.fulfill(). The most appropriate place would be in your AuthService mock, after the success and failure callbacks are run.
If I can suggest you something though, writing tests that asser whether certain methods have been called is not a safe approach to testing, as you are just testing implementation rather than behaviour.
What could be a better approach is to test the two componets independently. TestAuthService making sure that both the success and failure path execute as expected, and the DisplayHelper to make sure that the alert view is actually added to the view hierarchy.
This article could be a useful place to start to find out how unit test alerts, and this post is a good read on why and how to avoid mocks.
Below is some code I wrote:
func Begin(input: String) -> Double {
let inputs = (input as NSString).lowercaseString
var smth = AnalyzeInput(input: inputs)
return smth.findOutput()
}
Line 3 of this code (the 'smth' declaration) begins a long series of code that, at many instances, may throw some kind of an error (typically an index out of bounds error when creating a substring). Because this code is about 5,000 lines in 15 different files, and manually handling each exception would take a long time for each one, I would greatly prefer to not have to manually handle these exceptions.
I understand that there is no try / catch / finally structure in Swift, but is there anything where I can mimic that functionality? Or did I just dig myself into a giant hole?
If you're throwing exceptions in cases that are not catastrophic errors (i.e. you expect to crash soon), then you've dug yourself a hole. Cocoa does not use exceptions for general error handling. It uses them for truly exceptional situations. It's up to you to avoid index-out-of-bounds.
See the Error Handling Programming Guide for Cocoa for documentation on Cocoa error handling. Your function should likely look something like this:
func begin(input: String, error: NSErrorPtr) -> Double? {
let inputs = input.lowercaseString // "as NSString" is no longer needed
let smth = AnalyzeInput(input: inputs, error: NSErrorPtr) // pass along your error pointer
return smth?.findOutput() // Use ?. to return nil if smth is nil, or Double? otherwise
}
Some of us are exploring more functional approaches, such as a Result object. These are all still pretty experimental, but in principle it would look like this:
func begin(input: String) -> Result<Double, NSError> {
let inputs = input.lowercaseString
let smth = AnalyzeInput(input: inputs) // AnalyzeInput would return a Result
return smth.map { $0.findOutput() } // And then we map it to the final result
}
I Wrote a function that return two Strings, when calling the function regularly its works fine, but when I'm running the function through loop, I'm getting this error:
Thread 1: EXC_BAD_ACCESS (code=2, address=0xbfffcba0)
override func viewDidLoad()
{
super.viewDidLoad()
test()
}
func test()
{
var funcs = [checkButton]
var a = checkButton(value: 1) // Runs OK
for f in funcs{
var result = f(value: 1) // Fail
}
}
func checkButton(#value: Int) -> (location: String, pattern: String){
return ("abc","cba")
}
Update:
I'm using Xcode 6 beta 2, and running Mavericks on VMware Workstation.
Also, I've just created new clean project with that code and still getting the error.
This code runs fine for me. Your EXC_BAD_ACCESS must be coming from some other part of your code. Try setting a breakpoint and stepping through the code to find the line throwing the error.
From the “The Swift Programming Language.”
“An instance method can be called only on a specific instance of the type it belongs to. It cannot be called in isolation without an existing instance.”
checkButton() is an instance method, not a closure. It works in the first case because there is an implicit self. before checkButton(). It will not work in the second case.
If you want to make checkButton a closure you could declare it like so:
let checkButton = { (#value: Int) -> (location: String, pattern: String) in
return ("abc","cba")
}
I can confirm that it doesn't work for me either. Created a iOS single-view app from template, and added above code. crash. As an experiment, I took it out of the array (just f = self.checkButton) and got the same result.
I think it's a bug in the compiler.
First according to the book, a method is actually a function which is actually a closure, albeit one with special properties and restrictions. Shouldn't self.checkButton (or implicit version) be sufficient to "give it an existing instance", making it a closure? If MattL is correct that instance methods can't be used as closures, then the compiler shouldn't allow you to assign one to anything.
Second, the crash occurs on the exit, not on the call. And if you reference self in checkButton, (e.g. println(self.title) having previously set title), it works fine. That suggests that the instance is indeed known and operating, just something wrong on the return.
Third, changing it to a class method doesn't help. Changing these lines
var a = ViewController.checkButton(value: 1)
var funcs = [ViewController.checkButton]
class func checkButton(#value: Int) -> (location: String, pattern: String)
results in the same crash. I don't see any similar prohibition on context for class methods.
Fourth, if you simply change the return type from (location: String, pattern: String) to just String and return abc, then the whole thing works fine.
Fourth, if you wrap test and checkButton in a new class testClass, and then call it as below, it works:
class testClass {
func test()
{
var funcs = [checkButton]
var a = checkButton(value: 1) // Runs OK
for f in funcs {
var result = f(value: 1) // Fail
println(result)
}
}
func checkButton(#value: Int) -> (location: String, pattern: String){
return ("abc","cba")
}
}
class ViewController: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let g = testClass()
g.test()
}
}
Now change testClass to testClass: NSObject and it crashes.
So it looks like the compiler is botching up a method return when called as a closure with a tuple in a Obj-C subclass. When I put it like that, I must say that it's not terribly surprising nobody's noticed yet; you're really pushing the edge here!
More practically, in the meantime, if it's helpful, an alternative to changing your method to a closure is to keep it unchanged and just wrap it as you put it in the array:
var funcs = [{value in self.checkButton(value: value)}]
This seems to work.