Nimble: Comparing two ErrorTypes with equal()

Created on 30 Sep 2015  Â·  7Comments  Â·  Source: Quick/Nimble

So let me know if there's a better way to do this that I'm overlooking.

I have a situation in a unit test where I'm giving (Object1) a mock delegate (Mock1). I'm then calling an async function on Object1 that is going to notify Mock1, its delegate of an error (passing an ErrorType). In my test I am trying to confirm that the error that Mock1 received is a specific error.

The problem i'm running into is that ErrorType doesn't conform to Equatable, and I can't extend a protocol to conform to another protocol. Because of this, I can't say in the tests

expect(mockObject.receivedError).to(equal(ThisErrorEnum.SomeError))

I can compare the domains and the codes by hand in each test but that's not very elegant. Temporarily I put this in my test module:

func equal(expectedValue: ErrorType?) -> NonNilMatcherFunc<ErrorType> {
    return NonNilMatcherFunc { actualExpression, failureMessage in
        failureMessage.postfixMessage = "equal <\(stringify(expectedValue))>"
        let actualValue = try actualExpression.evaluate()
        if expectedValue == nil || actualValue == nil {
            if expectedValue == nil {
                failureMessage.postfixActual = " (use beNil() to match nils)"
            }
            return false
        }

        return expectedValue! == actualValue!
    }
}

func stringify(error: ErrorType?) -> String {
    return "Domain: \(error?._domain) | Code: \(error?._code)"
}

func ==(lhs: ErrorType, rhs: ErrorType) -> Bool {
   return (lhs._domain == rhs._domain) && (lhs._code == rhs._code)
}

So my question is whether there's an elegant way that already exists to make this comparison, and if not whether adding an equal function to handle ErrorTypes specifically would be useful.

enhancement help wanted

Most helpful comment

This has been merged into master (#260) using a matchError.

I'll close this issue when a release has been made.

All 7 comments

There's no implementation of equality for ErrorType in Nimble. A pull request (with tests) is appreciated though!

—
Sent from my iPhone

On Tue, Sep 29, 2015 at 3:59 PM, Scott Robbins [email protected]
wrote:

So let me know if there's a better way to do this that I'm overlooking.
I have a situation in a unit test where I'm giving (Object1) a mock delegate (Mock1). I'm then calling an async function on Object1 that is going to notify Mock1, its delegate of an error (passing an ErrorType). In my test I am trying to confirm that the error that Mock1 received is a specific error.
The problem i'm running into is that ErrorType doesn't conform to Equatable, and I can't extend a protocol to conform to another protocol. Because of this, I can't say in the tests

expect(mockObject.receivedError).to(equal(ThisErrorEnum.SomeError))

I can compare the domains and the codes by hand in each test but that's not very elegant. Temporarily I put this in my test module:

func equal(expectedValue: ErrorType?) -> NonNilMatcherFunc<ErrorType> {
    return NonNilMatcherFunc { actualExpression, failureMessage in
        failureMessage.postfixMessage = "equal <\(stringify(expectedValue))>"
        let actualValue = try actualExpression.evaluate()
        if expectedValue == nil || actualValue == nil {
            if expectedValue == nil {
                failureMessage.postfixActual = " (use beNil() to match nils)"
            }
            return false
        }

        return expectedValue! == actualValue!
    }
}
func stringify(error: ErrorType?) -> String {
    return "Domain: \(error?._domain) | Code: \(error?._code)"
}
func ==(lhs: ErrorType, rhs: ErrorType) -> Bool {
   return (lhs._domain == rhs._domain) && (lhs._code == rhs._code)
}

So my question is whether there's an elegant way that already exists to make this comparison, and if not whether adding an equal function to handle ErrorTypes specifically would be useful.

Reply to this email directly or view it on GitHub:
https://github.com/Quick/Nimble/issues/197

Created a PR with tests

It is quite common to override equality test in an enum if you have associated values

for example:

public enum Error: ErrorType {
    case Parsing(reason: String?)
    case Response(statusCode: Int)


    public var toNSError: NSError {
        return NSError(domain: _domain, code: _code, userInfo: userInfo)
    }

    private var userInfo: [NSObject : AnyObject]? {
        var localizedDescription: String?
        switch self {
        case .Parsing(let reason):
            localizedDescription = reason
        case .Response(let statusCode):
            localizedDescription = NSHTTPURLResponse.localizedStringForStatusCode(statusCode)
        }

        return localizedDescription.map { [NSLocalizedDescriptionKey: $0] }
    }
}

extension Error: Equatable {}

public func == (lhs: Error, rhs: Error) -> Bool {
    switch (lhs, rhs) {
    case (.Parsing(let lReason), .Parsing(let rReason)):
        return lReason == rReason
    case (.Response(let lStatusCode), .Response(let rStatusCode)):
        return lStatusCode == rStatusCode
    default:
        return false
    }
}

with these changes my equality tests are now failing because an enum is by default an ErrorType

context("when not equal") {
                    it("should not be equal") {
                        let error: Error = .Parsing(reason: "test")
                        let error2: Error = .Response(statusCode: 5)
                        expect(error) != error2
                    }

                    context("with different reason") {
                        it("should not be equal") {
                            let error: Error = .Parsing(reason: "test")
                            let error2: Error = .Parsing(reason: "another test")
                            expect(error) != error2
                        }
                    }
                }

Huh, hadn't tried doing that before. I'll have to think about if there's a good way to have the functionality of both. Any suggestions?

Not sure, enums don't inherit an existing type that can be overridden for this behavior. I'll revert this merge commit before release. I didn't consider this case, thanks @sync.

This has been merged into master (#260) using a matchError.

I'll close this issue when a release has been made.

Cut v4.0.0. Closing.

Was this page helpful?
0 / 5 - 0 ratings