The "eh... just about?" custom operator

Recently I’ve been writing a lot of model logic tests (oh tests = 😻, I’m a convert and will be talking to you about them at a party soon while you try desperately to catch anyone else’s eye and make your excuses). Anyway, there’s a bit of maths going on with Doubles, which means I’m ending up with a lot of these checks:

XCTAssertEqualWithAccuracy(calculatedResult, niceRoundNumber, accuracy: 0.001,
"These should have been just about equal")

These are just some tiny calculation differences (e.g. 50.00000000001 vs 50) that are just cognitive noise - I don’t care about the accuracy and it’s a lot of overhead for a rough comparison. Sounds like a job for a custom operator:

infix operator ~ { associativity left precedence 130 }

public func ~(lhs: Double?, rhs: Double?) -> Bool {
    switch (lhs, rhs) {
    case (let left?, let right?):
        let l = Double(round(accuracy * left)/accuracy)
        let r = Double(round(accuracy * right)/accuracy)
        return l == r
    default:
        return lhs == rhs
    }
}

private let accuracy = 1000.0 // compare down to the thousandth

And now my tests just look like:

XCTAssertTrue(calculatedResult ~ niceRoundNumber)

which I find a lot easier to write and read back. 👍

Update 28/03/16

Two things occured to me: firstly defining !~ would be nice:

infix operator !~ { associativity left precedence 130 }

public func !~(lhs: Double?, rhs: Double?) -> Bool {
    return !(lhs ~ rhs)
}

And secondly, I remembered this fine article from Joe Masilotti about manually triggering test failures and capturing their context with default #file and #line arguments. So now I can write an AssertMoreOrLessEqual function to produce a default message in the same format as XCTAssertEqual():

func AssertMoreOrLessEqual( lhs: Double?, _ rhs: Double?,
                            _ message: String? = nil,
                            file: String = #file, line: UInt = #line) {
    if lhs !~ rhs {
        let message = message ??
                      "AssertMoreOrLessEqual failed: \(lhs) is not more or less equal to \(rhs)"
        self.recordFailureWithDescription(message,
                               inFile: file, atLine: line, expected: true)
    }
 }

And now my tests look like:

Shows test failure message from custom function

🎉