Quick / Nimble

A Matcher Framework for Swift and Objective-C
https://quick.github.io/Nimble/documentation/nimble/
Apache License 2.0
4.79k stars 595 forks source link

Reexamine how Nimble treats nils in Swift code. #1124

Open younata opened 4 months ago

younata commented 4 months ago

This comes out of #1123 (thanks @jeslyvarghese!), which is specifically caused by... essentially confusion as to what should be the correct behavior of the equal matcher with regards to nil. See my comment here for more context.

Essentially, I think that we should fundamentally reexamine how nils are treated in Nimble. For example, the equal matcher should properly operate on nil. expect(nil as Int?).toNot(equal(0)) should pass, because nil is not equal to 0. expect(nil).to(equal(nil)) should also pass, because nil is equal to nil. We should do the same for a bunch of the other built-in matchers in Nimble.

Matchers to update:

Additionally, though, we should also take the opportunity to reexamine how Optionals are handled internally. I think that Matchers should be able to declare in the type system that they don't handle Optionals, and Nimble will fail the matcher if it receives nil. No more guard let value = try actualExpression.evaluate() else { return MatcherResult(.fail, message.appendedBeNilHint()) }. That should either be a runtime test failure, or even a compiler failure.

While we're at it, maybe also change how Expressions are handled? So that Optionality can be passed with them (right now, Expression/AsyncExpression essentially wrap () -> T?, regardless of whether T is an Optional<SomethingElse> or something else). In other words, passing a non-optional expression into expect or require should be a thing. This change feels a bit more radical, though.

Regardless, we should keep the current behavior when used with Objective-C, thanks to the fact that Objective-C doesn't have the concept of non-optional objects. This will require special-casing, but I think it'll be worth it.