Releases: Quick/Nimble
v7.1.0
Improvements
- Make
PredicateResult
members public for use in custom matchers #455 (Thanks @ishaanSejwal) - Implement
satisfyAllOf
matcher and&&
operator #511 (Thanks @wongzigii) - Xcode 9.3 support #513 (Thanks @ikesyo)
Bugfixes
- Fix
waitUntil
ignoreAsyncDefaults.Timeout
value #512 (Thanks @wongzigii)
Docs
v7.0.3
v7.0.2
This release fully supports Xcode 9 (both Swift 3.2 and Swift 4)! 🎉 This also contains a memory leak fix and some documentation updates.
Improvements
- Support both Swift 3.x and Swift 4 #457 (Thanks @ikesyo)
- Xcode 9 Support (Swift 3.2) #446 (Thanks @shaps80 and @ikesyo)
- Address redundant conformance warnings in Swift 4 #443 (Thanks @ikesyo)
Bugfixes
Docs
v7.0.1
A small release with a hotfix for Xcode 9 and some documentation upkeep.
Bugfixes
- Test in Swift 3 compatibility mode #431 (Thanks @sharplet)
- Fixes errors in Xcode 9 beta 1
'Cannot use mutating member on immutable value: 'generator' is a 'let' constant
- Fixes errors in Xcode 9 beta 1
- Fixed expectation regression & failure bugs #428 (Thanks @jeffh)
Docs
v7.0.0
Nimble 7 is released! This is part of a series of major version releases to support a major change undergoing in Nimble - specifically how custom matchers are written. If you don't use custom matchers, you should see little visible changes and can safely skip to the bottom to see the "Other Changes". But if you do use custom matchers or are interested in the technical details, continue reading.
In short, Nimble is replacing Matcher
protocol with Predicate
type. The Predicate
type attempts to address several common pitfalls when creating custom matchers and allow for several major features in the future:
- Protocol extension matchers:
expect(1).to.equal(1)
- Add more composable matchers:
expect([1, 2]).to(contain(1) && contain(2))
- Increase flexibility of existing composable matchers:
expect(1).to(equal(1) || beNil())
- Allow wrapping existing matchers like:
expect([1, 2]).to(contain(1).followed(by: 2))
But we're getting ahead of ourselves for a future that isn't here yet (or guaranteed). Let's focus back on Predicate
.
The New Predicate
Type
Nimble v7.0.0 introduces a new matcher API via the Predicate
type (we used all the matcher names). For the purposes of discussion, matcher refers to the concept Nimble has for building expectations and Matcher
refers to the (now) deprecated matcher API.
The main goal is to help make matcher writing less error-prone in several ways:
- Support special
nil
handling typical for Nimble matchers. - Make error messaging explicit and less shared-state munging.
- Be explicit about the trinary behavior matchers have instead of relying on bools.
Predicate
aims to address these goals by changing the closure to be a pure function that returns a custom PredicateResult
type. The PredicateResult
type is simply two values:
PredicateResult(status: .fail, message: .expectedActualValueTo("equal <\(expected)>"))
status
describes if the matcher succeeds or fails against the given value and message
is the structured textual representation of that status. status
is a trinary with the following values:
.matches
- similar to returning "true". Indicates the expectation passes..doesNotMatch
- similar to returning "false". Indicates the expectation failed, but negation would succeed (eg -toNot
).fail
- similar to returning "false". Indicates the expectation failed, even if negation was used.
This allows matchers to better indicate improper usages that would incorrectly pass if negated. If you need only the normal boolean-like return, use the alternative PredicateResult
constructor:
PredicateResult(bool: true, message: ...)
The message
argument supports a more structured error messaging that composable matchers can rely upon. It is currently limited, but is open for possible expansion if you file an issue.
Predicate
replaces the following existing Nimble types:
Matcher
- UsePredicate
instead.NonNilMatcherFunc
- UsePredicate
plus the.requireNonNil
methodMatcherFunc
- UsePredicate
instead.
Since Predicate
is not a protocol with an associated type, it makes function definitions much easier without requiring another type like NonNilMatcherFunc
:
// OLD METHOD:
// returning protocol
func equal<M: Matcher, T: Equatable where T == M.ValueType>(_ expected: T?) -> M { ... }
// OLD METHOD:
// using NonNilMatcherFunc or MatcherFunc
func equal<T: Equatable>(_ expected: T?) -> NonNilMatcherFunc<T> { ... }
// NEW METHOD
func equal<T: Equatable>(_ expected: T?) -> Predicate<T> { ... }
Removing the protocol with associated type allows more composability among matchers. Previous these function types made composing them difficult:
// OLD METHOD:
func firstMatcher<M: Matcher, T: Equatable where T == M.ValueType>(_ expected: T?) -> M { ... }
func secondMatcher<T: Equatable>(_ expected: T?) -> NonNilMatcherFunc<T> { ... }
// can only specify every matcher in the generic
func usesOnlyTwoMatchers<M1: Matcher, M2: Matcher where M1.ValueType == M2.ValueType>(first: M1, second: M2) -> M
// NEW METHOD:
func firstMatcher<T: Equatable>(_ expected: T?) -> Predicate<T> { ... }
func secondMatcher<T: Equatable>(_ expected: T?) -> Predicate<T> { ... }
// can support many matchers
func usesManyMatchers<T>(matchers: [Predicate<T>]) -> Predicate<T>
Migrating to Predicate
If you're not using custom matchers, there isn't any additional work for you 🎉.
To migrate existing matchers, Nimble v7.0.0 currently provides temporary constructors:
/// These constructors are for convenience in adopting Nimble v7.0.0, but will be removed in Nimble v9.0.0
// If you already have a Matcher type you need to simply convert to Predicate:
Predicate.fromDeprecatedMatcher(myMatcher)
// Alternatively, you can use the extension:
myMatcher.predicate
// If you want to construct a Predicate using the same arguments as MatcherFunc
Predicate.fromDeprecatedClosure { actualExpression, failureMessage -> Bool in ... }
// If you want to construct a Predicate using the same arguments as NonNilMatcherFunc
Predicate.fromDeprecatedClosure { actualExpression, failureMessage -> Bool in
...
}.requireNonNil
These are useful to quickly adopt Nimble v7.0.0, but it's better in the long run to adopt the proper Predicate
API to last beyond Nimble v9.0.0:
// The simple way to implment a NonNilMatcherFunc matcher in the new Predicate way:
public func equal<T: Equatable>(_ expectedValue: T?) -> Predicate<T> {
// Alternatively, you can use Predicate.simple if you don't modify `msg`.
// When a string is given to .define(), then
// msg = .expectedActualValueTo("equal <\(expectedValue)>")
// Predicate.define and Predicate.simple imply calling .requireNonNil.
Predicate.define("equal <\(expectedValue)>") { actualExpression, msg -> PredicateResult in
let actualValue = try actualExpression.evaluate()
let matches = actualValue == expectedValue && expectedValue != nil
if expectedValue == nil || actualValue == nil {
if expectedValue == nil && actualValue != nil {
return PredicateResult(
status: .fail,
message: msg.appendedBeNilHint()
)
}
return PredicateResult(status: .fail, message: msg)
}
return PredicateResult(status: PredicateStatus(bool: matches), message: msg)
}
}
// For a MatcherFunc:
public func beNil<T>() -> Predicate<T> {
// Alternatively, you can use Predicate.defineNilable
return Predicate.simpleNilable("be nil") { actualExpression -> PredicateStatus in
let actualValue = try actualExpression.evaluate()
return PredicateStatus(bool: actualValue == nil)
}
}
To ease the migration process, Nimble will be removing Matcher
after several major versions:
- v7.x.x will introduce a new replacement to
Matcher
family of types via thePredicate
type.Matcher
,NonNilMatcherFunc
,MatcherFunc
are marked as deprecated- Introduces temporary, migration-friendly constructors on
Predicate
to help suppress warnings for now.
- v8.x.x will deprecate the temporary, migration friendly constructors.
- v9.x.x will remove the old matcher types and temporary migration-friendly constructors.
New features that are released pre-v9.x.x will cater to the newer Predicate
style.
Also, all built-in Nimble matchers currently use Predicate
and Predicate
also implemented the Matcher
type until it's removal.
Other Changes
toSucceed
Matcher
For quick, inline matchers, you can use toSucceed
with the ToSucceedResult
enum:
expect {
return .succeeded
}.to(succeed())
expect {
return .failed(reason: "expected a closure, got <nil> (use beNil() to match nils)")
}.to(succeed())
Changelog
- (#390) Introduce new Matcher API (aka, Predicates)
- (#417) Fix optional binding violation for SwiftLint (thanks @ysk-tngc)
- (#410) Adds
toSucceed
matcher (thanks @Rivukis) - (#420) Fix invalid reference to CwlCatchBadInstructionPOSIX.swift for case-sensitive file systems (thanks @dagio)
As always, happy testing 😎!
v6.1.0
This is a relatively small release that addresses issues for Xcode 8.3 / Swift 3.1. Merry Testing! 👾
New:
- Add float and double overloads to
beGreaterThan
for ObjC (Thanks @soranoba)
Changes:
- SwiftLint no longer runs for framework targets. Instead an internal SwiftLint target is used instead. (Thanks @wongzigii)
Bugfixes:
- Fixes
throwError
matcher with the latest version of Swift (Thanks @aaroncrespo) - Fixes
throwError
matcher warning for Xcode 8.3
Nimble v6.0.1
A small bugfix release to fix an installation issue for Carthage users that
have SwiftLint installed.
Nimble v6.0.0
This release includes one small breaking change and new features in its Matchers. Happy testing 🎉!
Breaking Changes:
- Renamed framework target from
Nimble-OSX
toNimble-macOS
- Thanks Zigii Wong (@wongzigii)!
New Features:
- Add
containObjectSatisfying
matcher to perform order-indifferent check on collections - Thanks Jared Friese (@jwfriese)! beKindOf
matcher now supports Swift-only types - Thanks Jared Friese (@jwfriese)!
Fixes:
- Stylististic changes for SwiftLint (still on-going) - Thanks Yurii Samsoniuk (@sigito)!
- Also don't print warning about SwiftLint not being installed
- Fix Nimble not running in Swift-only environments - Thanks Syo Ikeda (@ikesyo)!
New Features
containElementSatisfying matcher
Add containElementSatisfying
matcher to perform order-indifferent check on
collections. This is convenient if you require at least one element in a
collection to pass a predicate.
// swift
expect([1, 2, 3]).to(containElementSatisfying { n in
return n == 2
})
// objc
expect(@[@1, @2, @3]).to(containElementSatisfying(^BOOL(id n) {
return [n isEqualToNumber:@2];
}));
beAKindOf and beAnInstanceOf now operates on Swift-only types
The beAKindOf
and beAnInstanceOf
matchers has been updated to support
Swift-only types. That means this is now possible:
// swift
expect("hello world").to(beAKindOf(String.self))
expect("hello world").to(beAnInstanceOf(String.self))
In older versions of Swift, casting was always self-evident and the compiler would
disallow potentially invalid coercions (eg - Any to String). But this is no
longer the case and these matchers have been updated.
Also, beAKindOf
is now available in Swift-only environments (aka - Linux).
Nimble v5.1.1
This release is only a bugfix release.
- Fixes umbrella header import for macOS and tvOS
Nimble v5.1.0
This release includes new features and shouldn't break backwards compatibility
with 5.0.0. Thus, 5.1.0 supports Swift 3 / Xcode 8. The TL;DR list of changes:
New Features:
- Add
throwAssertion
matcher to test precondition failures. Thanks @mattgallagher and @abbeycode! - Objective-C supports some C literals
Fixes:
- The regular expression
match
matcher is available for Linux. Thanks @ikesyo! - Document about
AsyncDefaults
. Thanks @wongzigii! - Fix
matchError
incorrectly succeeding on any Error type given.
New Feature Details
ThrowAssertion Matcher
Nimble now includes a throwAssertion
matcher to test if a given piece of code
fails precondition
or invokes fatalError
.
// swift
expect { precondition(false, "this will fail") }.to(throwAssertion())
// type-hint that this closure does not return anything
expect { () -> Void in fatalError() }.to(throwAssertion())
Due to the inherit implementation of preconditions and fatalErrors, there are
several limitations:
- This is only available for Swift.
- There is no validation of the failure message.
- This only works for x86 architectures. This means Simulators + macOS only.
- tvOS simulator requires you to disable "Debug executable" in your scheme's test configuration.
Nimble embeds CwlPreconditionTesting to implement throwAssertion
. Special
thanks to @mattgallagher for CwlPreconditionTesting and @abbeycode for the bulk
of the adding the matcher for Nimble.
Go test those assertions!
Objective-C: Limited C-Literals Support
Objective-C version of Nimble now can automatically box certain C types for
you. This means you don't need to wrap them yourself:
// objective-c
expect(1).to(equal(2));
expect(98.6).to(beCloseTo(98.6));
expect(YES).to(beTrue());
expect(@[@1, @2]).to(haveCount(2));
expect("hello").toNot(equal("world"));
expect(NSMakeRange(0, 5)).to(equal(NSMakeRange(0, 5)));
Currently, not all matchers support all types. The following matchers support C types:
equal
beGreaterThan
beGreaterThanOrEqual
beLessThan
beLessThanOrEqual
beCloseTo
beTrue
beFalse
beTruthy
beFalsy
haveCount
The C types each supports is matcher specific. But there's here's the basic heuristics:
- C numeric types are boxed as
NSNumber *
. (eg -uint64_t -> NSNumber *
) - C booleans are boxed as
NSNumber *
. (eg -BOOL -> NSNumber *
) - C strings are boxed as
NSString *
(eg -char * -> NSString *
) NSRange
are boxed asNSValue *
(eg -NSRange -> NSValue *
)
While they shouldn't cause too many suprises in practice, note that you still
can have some gotchas with everything boxing as NSNumber *
(eg -
expect(1).to(beTrue())
passes).
Please file an issue if you want more matchers.
Happy testing! 😁