Dov
Dov

Reputation: 16166

Mocking a String-class method in Swift

I've got some Swift 2.0 code, for which I'm trying to achieve 100% code coverage. I'm doing some JSON handling, part of which looks like so:

guard let jsonData = jsonText.dataUsingEncoding(NSUTF8StringEncoding) else {
    throw ErrorCode.JSONEncodingFailure
}

I don't think there's a real-world case in which any string can't be encoded as UTF-8, but I don't want to open myself up to a crashing error either, so that code must remain. How can I mock the jsonText object to return nil for dataUsingEncoding()?

The closest I've come is to subclass NSString like so:

public class BadString: NSString {

    public override var length: Int {
        get {
            return 5
        }
    }

    public override func characterAtIndex(index: Int) -> unichar {
        return  0
    }

    public override func dataUsingEncoding(encoding: NSStringEncoding) -> NSData? {
        return nil
    }
}

Here's the problem, though. In order for my mock implementation to be used, I have to define jsonText (a function parameter) as an NSString, rather than String, which feels wrong for an all-Swift codebase. With it defined as a Swift String, I have to cast my BadString to that type, and it uses the String implementation instead of my own.

Is there another (clean) way to achieve this?

Upvotes: 2

Views: 447

Answers (1)

fqdn
fqdn

Reputation: 2843

You will be hard-pressed to find a string that cannot be encoded using UTF-8! As long as you know that is the encoding you will be using, I would suggest that you not worry about testing the "encoding failure" case.

However, if you still desire to test it then I recommend making one of the following changes in order to allow you to do so:

(1) change the way you are thinking about the 'failure': if you know that the string you are encoding will always be non-empty, then broaden the guard to also require that the encoded data has length > 0, e.g. =>

guard let jsonData = jsonText.dataUsingEncoding(NSUTF8StringEncoding)
        where jsonData.length > 0 else {
    throw ErrorCode.JSONEncodingFailure
}

...using this idea, you can now use an empty string for jsonText and trigger this code path (assuming that an empty string would also satisfy your definition of 'failure' here)

(2) store your string encoding value in a variable (let's call it stringEncoding) that you can access during your test setup, and then test this using incompatible values for jsonText and stringEncoding, e.g. =>

var jsonText = "🙈"
let stringEncoding = NSASCIIStringEncoding

...I guarantee that jsonText.dataUsingEncoding(stringEncoding) will return nil in this case :)

Happy Testing! I hope this helps!

Upvotes: 1

Related Questions