A Matcher Framework for Swift and Objective-C

Overview

Nimble

Build Status CocoaPods Carthage Compatible Accio supported Platforms

Use Nimble to express the expected outcomes of Swift or Objective-C expressions. Inspired by Cedar.

// Swift
expect(1 + 1).to(equal(2))
expect(1.2).to(beCloseTo(1.1, within: 0.1))
expect(3) > 2
expect("seahorse").to(contain("sea"))
expect(["Atlantic", "Pacific"]).toNot(contain("Mississippi"))
expect(ocean.isClean).toEventually(beTruthy())

How to Use Nimble

Table of Contents generated with DocToc

Some Background: Expressing Outcomes Using Assertions in XCTest

Apple's Xcode includes the XCTest framework, which provides assertion macros to test whether code behaves properly. For example, to assert that 1 + 1 = 2, XCTest has you write:

// Swift

XCTAssertEqual(1 + 1, 2, "expected one plus one to equal two")

Or, in Objective-C:

// Objective-C

XCTAssertEqual(1 + 1, 2, @"expected one plus one to equal two");

XCTest assertions have a couple of drawbacks:

  1. Not enough macros. There's no easy way to assert that a string contains a particular substring, or that a number is less than or equal to another.
  2. It's hard to write asynchronous tests. XCTest forces you to write a lot of boilerplate code.

Nimble addresses these concerns.

Nimble: Expectations Using expect(...).to

Nimble allows you to express expectations using a natural, easily understood language:

// Swift

import Nimble

expect(seagull.squawk).to(equal("Squee!"))
// Objective-C

@import Nimble;

expect(seagull.squawk).to(equal(@"Squee!"));

The expect function autocompletes to include file: and line:, but these parameters are optional. Use the default values to have Xcode highlight the correct line when an expectation is not met.

To perform the opposite expectation--to assert something is not equal--use toNot or notTo:

// Swift

import Nimble

expect(seagull.squawk).toNot(equal("Oh, hello there!"))
expect(seagull.squawk).notTo(equal("Oh, hello there!"))
// Objective-C

@import Nimble;

expect(seagull.squawk).toNot(equal(@"Oh, hello there!"));
expect(seagull.squawk).notTo(equal(@"Oh, hello there!"));

Custom Failure Messages

Would you like to add more information to the test's failure messages? Use the description optional argument to add your own text:

// Swift

expect(1 + 1).to(equal(3))
// failed - expected to equal <3>, got <2>

expect(1 + 1).to(equal(3), description: "Make sure libKindergartenMath is loaded")
// failed - Make sure libKindergartenMath is loaded
// expected to equal <3>, got <2>

Or the *WithDescription version in Objective-C:

// Objective-C

@import Nimble;

expect(@(1+1)).to(equal(@3));
// failed - expected to equal <3.0000>, got <2.0000>

expect(@(1+1)).toWithDescription(equal(@3), @"Make sure libKindergartenMath is loaded");
// failed - Make sure libKindergartenMath is loaded
// expected to equal <3.0000>, got <2.0000>

Type Safety

Nimble makes sure you don't compare two types that don't match:

// Swift

// Does not compile:
expect(1 + 1).to(equal("Squee!"))

Nimble uses generics--only available in Swift--to ensure type correctness. That means type checking is not available when using Nimble in Objective-C. 😭

Operator Overloads

Tired of so much typing? With Nimble, you can use overloaded operators like == for equivalence, or > for comparisons:

// Swift

// Passes if squawk does not equal "Hi!":
expect(seagull.squawk) != "Hi!"

// Passes if 10 is greater than 2:
expect(10) > 2

Operator overloads are only available in Swift, so you won't be able to use this syntax in Objective-C. 💔

Lazily Computed Values

The expect function doesn't evaluate the value it's given until it's time to match. So Nimble can test whether an expression raises an exception once evaluated:

// Swift

// Note: Swift currently doesn't have exceptions.
//       Only Objective-C code can raise exceptions
//       that Nimble will catch.
//       (see https://github.com/Quick/Nimble/issues/220#issuecomment-172667064)
let exception = NSException(
    name: NSInternalInconsistencyException,
    reason: "Not enough fish in the sea.",
    userInfo: ["something": "is fishy"])
expect { exception.raise() }.to(raiseException())

// Also, you can customize raiseException to be more specific
expect { exception.raise() }.to(raiseException(named: NSInternalInconsistencyException))
expect { exception.raise() }.to(raiseException(
    named: NSInternalInconsistencyException,
    reason: "Not enough fish in the sea"))
expect { exception.raise() }.to(raiseException(
    named: NSInternalInconsistencyException,
    reason: "Not enough fish in the sea",
    userInfo: ["something": "is fishy"]))

Objective-C works the same way, but you must use the expectAction macro when making an expectation on an expression that has no return value:

// Objective-C

NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException
                                                 reason:@"Not enough fish in the sea."
                                               userInfo:nil];
expectAction(^{ [exception raise]; }).to(raiseException());

// Use the property-block syntax to be more specific.
expectAction(^{ [exception raise]; }).to(raiseException().named(NSInternalInconsistencyException));
expectAction(^{ [exception raise]; }).to(raiseException().
    named(NSInternalInconsistencyException).
    reason("Not enough fish in the sea"));
expectAction(^{ [exception raise]; }).to(raiseException().
    named(NSInternalInconsistencyException).
    reason("Not enough fish in the sea").
    userInfo(@{@"something": @"is fishy"}));

// You can also pass a block for custom matching of the raised exception
expectAction(exception.raise()).to(raiseException().satisfyingBlock(^(NSException *exception) {
    expect(exception.name).to(beginWith(NSInternalInconsistencyException));
}));

C Primitives

Some testing frameworks make it hard to test primitive C values. In Nimble, it just works:

// Swift

let actual: CInt = 1
let expectedValue: CInt = 1
expect(actual).to(equal(expectedValue))

In fact, Nimble uses type inference, so you can write the above without explicitly specifying both types:

// Swift

expect(1 as CInt).to(equal(1))

In Objective-C, Nimble only supports Objective-C objects. To make expectations on primitive C values, wrap then in an object literal:

expect(@(1 + 1)).to(equal(@2));

Asynchronous Expectations

In Nimble, it's easy to make expectations on values that are updated asynchronously. Just use toEventually or toEventuallyNot:

// Swift
DispatchQueue.main.async {
    ocean.add("dolphins")
    ocean.add("whales")
}
expect(ocean).toEventually(contain("dolphins", "whales"))
// Objective-C

dispatch_async(dispatch_get_main_queue(), ^{
    [ocean add:@"dolphins"];
    [ocean add:@"whales"];
});
expect(ocean).toEventually(contain(@"dolphins", @"whales"));

Note: toEventually triggers its polls on the main thread. Blocking the main thread will cause Nimble to stop the run loop. This can cause test pollution for whatever incomplete code that was running on the main thread. Blocking the main thread can be caused by blocking IO, calls to sleep(), deadlocks, and synchronous IPC.

In the above example, ocean is constantly re-evaluated. If it ever contains dolphins and whales, the expectation passes. If ocean still doesn't contain them, even after being continuously re-evaluated for one whole second, the expectation fails.

Sometimes it takes more than a second for a value to update. In those cases, use the timeout parameter:

// Swift

// Waits three seconds for ocean to contain "starfish":
expect(ocean).toEventually(contain("starfish"), timeout: .seconds(3))

// Evaluate someValue every 0.2 seconds repeatedly until it equals 100, or fails if it timeouts after 5.5 seconds.
expect(someValue).toEventually(equal(100), timeout: .milliseconds(5500), pollInterval: .milliseconds(200))
// Objective-C

// Waits three seconds for ocean to contain "starfish":
expect(ocean).withTimeout(3).toEventually(contain(@"starfish"));

You can also provide a callback by using the waitUntil function:

// Swift

waitUntil { done in
    ocean.goFish { success in
        expect(success).to(beTrue())
        done()
    }
}
// Objective-C

waitUntil(^(void (^done)(void)){
    [ocean goFishWithHandler:^(BOOL success){
        expect(success).to(beTrue());
        done();
    }];
});

waitUntil also optionally takes a timeout parameter:

// Swift

waitUntil(timeout: .seconds(10)) { done in
    ocean.goFish { success in
        expect(success).to(beTrue())
        done()
    }
}
// Objective-C

waitUntilTimeout(10, ^(void (^done)(void)){
    [ocean goFishWithHandler:^(BOOL success){
        expect(success).to(beTrue());
        done();
    }];
});

Note: waitUntil triggers its timeout code on the main thread. Blocking the main thread will cause Nimble to stop the run loop to continue. This can cause test pollution for whatever incomplete code that was running on the main thread. Blocking the main thread can be caused by blocking IO, calls to sleep(), deadlocks, and synchronous IPC.

In some cases (e.g. when running on slower machines) it can be useful to modify the default timeout and poll interval values. This can be done as follows:

// Swift

// Increase the global timeout to 5 seconds:
Nimble.AsyncDefaults.timeout = .seconds(5)

// Slow the polling interval to 0.1 seconds:
Nimble.AsyncDefaults.pollInterval = .milliseconds(100)

Objective-C Support

Nimble has full support for Objective-C. However, there are two things to keep in mind when using Nimble in Objective-C:

  1. All parameters passed to the expect function, as well as matcher functions like equal, must be Objective-C objects or can be converted into an NSObject equivalent:

    // Objective-C
    
    @import Nimble;
    
    expect(@(1 + 1)).to(equal(@2));
    expect(@"Hello world").to(contain(@"world"));
    
    // Boxed as NSNumber *
    expect(2).to(equal(2));
    expect(1.2).to(beLessThan(2.0));
    expect(true).to(beTruthy());
    
    // Boxed as NSString *
    expect("Hello world").to(equal("Hello world"));
    
    // Boxed as NSRange
    expect(NSMakeRange(1, 10)).to(equal(NSMakeRange(1, 10)));
  2. To make an expectation on an expression that does not return a value, such as -[NSException raise], use expectAction instead of expect:

    // Objective-C
    
    expectAction(^{ [exception raise]; }).to(raiseException());

The following types are currently converted to an NSObject type:

  • C Numeric types are converted to NSNumber *
  • NSRange is converted to NSValue *
  • char * is converted to NSString *

For the following matchers:

  • equal
  • beGreaterThan
  • beGreaterThanOrEqual
  • beLessThan
  • beLessThanOrEqual
  • beCloseTo
  • beTrue
  • beFalse
  • beTruthy
  • beFalsy
  • haveCount

If you would like to see more, file an issue.

Disabling Objective-C Shorthand

Nimble provides a shorthand for expressing expectations using the expect function. To disable this shorthand in Objective-C, define the NIMBLE_DISABLE_SHORT_SYNTAX macro somewhere in your code before importing Nimble:

#define NIMBLE_DISABLE_SHORT_SYNTAX 1

@import Nimble;

NMB_expect(^{ return seagull.squawk; }, __FILE__, __LINE__).to(NMB_equal(@"Squee!"));

Disabling the shorthand is useful if you're testing functions with names that conflict with Nimble functions, such as expect or equal. If that's not the case, there's no point in disabling the shorthand.

Built-in Matcher Functions

Nimble includes a wide variety of matcher functions.

Type Checking

Nimble supports checking the type membership of any kind of object, whether Objective-C conformant or not:

// Swift

protocol SomeProtocol{}
class SomeClassConformingToProtocol: SomeProtocol{}
struct SomeStructConformingToProtocol: SomeProtocol{}

// The following tests pass
expect(1).to(beAKindOf(Int.self))
expect("turtle").to(beAKindOf(String.self))

let classObject = SomeClassConformingToProtocol()
expect(classObject).to(beAKindOf(SomeProtocol.self))
expect(classObject).to(beAKindOf(SomeClassConformingToProtocol.self))
expect(classObject).toNot(beAKindOf(SomeStructConformingToProtocol.self))

let structObject = SomeStructConformingToProtocol()
expect(structObject).to(beAKindOf(SomeProtocol.self))
expect(structObject).to(beAKindOf(SomeStructConformingToProtocol.self))
expect(structObject).toNot(beAKindOf(SomeClassConformingToProtocol.self))
// Objective-C

// The following tests pass
NSMutableArray *array = [NSMutableArray array];
expect(array).to(beAKindOf([NSArray class]));
expect(@1).toNot(beAKindOf([NSNull class]));

Objects can be tested for their exact types using the beAnInstanceOf matcher:

// Swift

protocol SomeProtocol{}
class SomeClassConformingToProtocol: SomeProtocol{}
struct SomeStructConformingToProtocol: SomeProtocol{}

// Unlike the 'beKindOf' matcher, the 'beAnInstanceOf' matcher only
// passes if the object is the EXACT type requested. The following
// tests pass -- note its behavior when working in an inheritance hierarchy.
expect(1).to(beAnInstanceOf(Int.self))
expect("turtle").to(beAnInstanceOf(String.self))

let classObject = SomeClassConformingToProtocol()
expect(classObject).toNot(beAnInstanceOf(SomeProtocol.self))
expect(classObject).to(beAnInstanceOf(SomeClassConformingToProtocol.self))
expect(classObject).toNot(beAnInstanceOf(SomeStructConformingToProtocol.self))

let structObject = SomeStructConformingToProtocol()
expect(structObject).toNot(beAnInstanceOf(SomeProtocol.self))
expect(structObject).to(beAnInstanceOf(SomeStructConformingToProtocol.self))
expect(structObject).toNot(beAnInstanceOf(SomeClassConformingToProtocol.self))

Equivalence

// Swift

// Passes if 'actual' is equivalent to 'expected':
expect(actual).to(equal(expected))
expect(actual) == expected

// Passes if 'actual' is not equivalent to 'expected':
expect(actual).toNot(equal(expected))
expect(actual) != expected
// Objective-C

// Passes if 'actual' is equivalent to 'expected':
expect(actual).to(equal(expected))

// Passes if 'actual' is not equivalent to 'expected':
expect(actual).toNot(equal(expected))

Values must be Equatable, Comparable, or subclasses of NSObject. equal will always fail when used to compare one or more nil values.

Identity

// Swift

// Passes if 'actual' has the same pointer address as 'expected':
expect(actual).to(beIdenticalTo(expected))
expect(actual) === expected

// Passes if 'actual' does not have the same pointer address as 'expected':
expect(actual).toNot(beIdenticalTo(expected))
expect(actual) !== expected

It is important to remember that beIdenticalTo only makes sense when comparing types with reference semantics, which have a notion of identity. In Swift, that means types that are defined as a class.

This matcher will not work when comparing types with value semantics such as those defined as a struct or enum. If you need to compare two value types, consider what it means for instances of your type to be identical. This may mean comparing individual properties or, if it makes sense to do so, conforming your type to Equatable and using Nimble's equivalence matchers instead.

// Objective-C

// Passes if 'actual' has the same pointer address as 'expected':
expect(actual).to(beIdenticalTo(expected));

// Passes if 'actual' does not have the same pointer address as 'expected':
expect(actual).toNot(beIdenticalTo(expected));

Comparisons

// Swift

expect(actual).to(beLessThan(expected))
expect(actual) < expected

expect(actual).to(beLessThanOrEqualTo(expected))
expect(actual) <= expected

expect(actual).to(beGreaterThan(expected))
expect(actual) > expected

expect(actual).to(beGreaterThanOrEqualTo(expected))
expect(actual) >= expected
// Objective-C

expect(actual).to(beLessThan(expected));
expect(actual).to(beLessThanOrEqualTo(expected));
expect(actual).to(beGreaterThan(expected));
expect(actual).to(beGreaterThanOrEqualTo(expected));

Values given to the comparison matchers above must implement Comparable.

Because of how computers represent floating point numbers, assertions that two floating point numbers be equal will sometimes fail. To express that two numbers should be close to one another within a certain margin of error, use beCloseTo:

// Swift

expect(actual).to(beCloseTo(expected, within: delta))
// Objective-C

expect(actual).to(beCloseTo(expected).within(delta));

For example, to assert that 10.01 is close to 10, you can write:

// Swift

expect(10.01).to(beCloseTo(10, within: 0.1))
// Objective-C

expect(@(10.01)).to(beCloseTo(@10).within(0.1));

There is also an operator shortcut available in Swift:

// Swift

expect(actual)  expected
expect(actual)  (expected, delta)

(Type option+x to get on a U.S. keyboard)

The former version uses the default delta of 0.0001. Here is yet another way to do this:

// Swift

expect(actual)  expected ± delta
expect(actual) == expected ± delta

(Type option+shift+= to get ± on a U.S. keyboard)

If you are comparing arrays of floating point numbers, you'll find the following useful:

// Swift

expect([0.0, 2.0])  [0.0001, 2.0001]
expect([0.0, 2.0]).to(beCloseTo([0.1, 2.1], within: 0.1))

Values given to the beCloseTo matcher must be coercable into a Double.

Types/Classes

// Swift

// Passes if 'instance' is an instance of 'aClass':
expect(instance).to(beAnInstanceOf(aClass))

// Passes if 'instance' is an instance of 'aClass' or any of its subclasses:
expect(instance).to(beAKindOf(aClass))
// Objective-C

// Passes if 'instance' is an instance of 'aClass':
expect(instance).to(beAnInstanceOf(aClass));

// Passes if 'instance' is an instance of 'aClass' or any of its subclasses:
expect(instance).to(beAKindOf(aClass));

Instances must be Objective-C objects: subclasses of NSObject, or Swift objects bridged to Objective-C with the @objc prefix.

For example, to assert that dolphin is a kind of Mammal:

// Swift

expect(dolphin).to(beAKindOf(Mammal))
// Objective-C

expect(dolphin).to(beAKindOf([Mammal class]));

beAnInstanceOf uses the -[NSObject isMemberOfClass:] method to test membership. beAKindOf uses -[NSObject isKindOfClass:].

Truthiness

// Passes if 'actual' is not nil, true, or an object with a boolean value of true:
expect(actual).to(beTruthy())

// Passes if 'actual' is only true (not nil or an object conforming to Boolean true):
expect(actual).to(beTrue())

// Passes if 'actual' is nil, false, or an object with a boolean value of false:
expect(actual).to(beFalsy())

// Passes if 'actual' is only false (not nil or an object conforming to Boolean false):
expect(actual).to(beFalse())

// Passes if 'actual' is nil:
expect(actual).to(beNil())
// Objective-C

// Passes if 'actual' is not nil, true, or an object with a boolean value of true:
expect(actual).to(beTruthy());

// Passes if 'actual' is only true (not nil or an object conforming to Boolean true):
expect(actual).to(beTrue());

// Passes if 'actual' is nil, false, or an object with a boolean value of false:
expect(actual).to(beFalsy());

// Passes if 'actual' is only false (not nil or an object conforming to Boolean false):
expect(actual).to(beFalse());

// Passes if 'actual' is nil:
expect(actual).to(beNil());

Swift Assertions

If you're using Swift, you can use the throwAssertion matcher to check if an assertion is thrown (e.g. fatalError()). This is made possible by @mattgallagher's CwlPreconditionTesting library.

// Swift

// Passes if 'somethingThatThrows()' throws an assertion, 
// such as by calling 'fatalError()' or if a precondition fails:
expect { try somethingThatThrows() }.to(throwAssertion())
expect { () -> Void in fatalError() }.to(throwAssertion())
expect { precondition(false) }.to(throwAssertion())

// Passes if throwing an NSError is not equal to throwing an assertion:
expect { throw NSError(domain: "test", code: 0, userInfo: nil) }.toNot(throwAssertion())

// Passes if the code after the precondition check is not run:
var reachedPoint1 = false
var reachedPoint2 = false
expect {
    reachedPoint1 = true
    precondition(false, "condition message")
    reachedPoint2 = true
}.to(throwAssertion())

expect(reachedPoint1) == true
expect(reachedPoint2) == false

Notes:

  • This feature is only available in Swift.
  • It is only supported for x86_64 binaries, meaning you cannot run this matcher on iOS devices, only simulators.
  • The tvOS simulator is supported, but using a different mechanism, requiring you to turn off the Debug executable scheme setting for your tvOS scheme's Test configuration.

Swift Error Handling

You can use the throwError matcher to check if an error is thrown.

// Swift

// Passes if 'somethingThatThrows()' throws an 'Error':
expect { try somethingThatThrows() }.to(throwError())

// Passes if 'somethingThatThrows()' throws an error within a particular domain:
expect { try somethingThatThrows() }.to(throwError { (error: Error) in
    expect(error._domain).to(equal(NSCocoaErrorDomain))
})

// Passes if 'somethingThatThrows()' throws a particular error enum case:
expect { try somethingThatThrows() }.to(throwError(NSCocoaError.PropertyListReadCorruptError))

// Passes if 'somethingThatThrows()' throws an error of a particular type:
expect { try somethingThatThrows() }.to(throwError(errorType: NimbleError.self))

When working directly with Error values, using the matchError matcher allows you to perform certain checks on the error itself without having to explicitly cast the error.

The matchError matcher allows you to check whether or not the error:

  • is the same type of error you are expecting.
  • represents a particular error value that you are expecting.

This can be useful when using Result or Promise types, for example.

// Swift

let actual: Error = ...

// Passes if 'actual' represents any error value from the NimbleErrorEnum type:
expect(actual).to(matchError(NimbleErrorEnum.self))

// Passes if 'actual' represents the case 'timeout' from the NimbleErrorEnum type:
expect(actual).to(matchError(NimbleErrorEnum.timeout))

// Passes if 'actual' contains an NSError equal to the one provided:
expect(actual).to(matchError(NSError(domain: "err", code: 123, userInfo: nil)))

Note: This feature is only available in Swift.

Exceptions

// Swift

// Passes if 'actual', when evaluated, raises an exception:
expect(actual).to(raiseException())

// Passes if 'actual' raises an exception with the given name:
expect(actual).to(raiseException(named: name))

// Passes if 'actual' raises an exception with the given name and reason:
expect(actual).to(raiseException(named: name, reason: reason))

// Passes if 'actual' raises an exception which passes expectations defined in the given closure:
// (in this case, if the exception's name begins with "a r")
expect { exception.raise() }.to(raiseException { (exception: NSException) in
    expect(exception.name).to(beginWith("a r"))
})
// Objective-C

// Passes if 'actual', when evaluated, raises an exception:
expect(actual).to(raiseException())

// Passes if 'actual' raises an exception with the given name
expect(actual).to(raiseException().named(name))

// Passes if 'actual' raises an exception with the given name and reason:
expect(actual).to(raiseException().named(name).reason(reason))

// Passes if 'actual' raises an exception and it passes expectations defined in the given block:
// (in this case, if name begins with "a r")
expect(actual).to(raiseException().satisfyingBlock(^(NSException *exception) {
    expect(exception.name).to(beginWith(@"a r"));
}));

Note: Swift currently doesn't have exceptions (see #220). Only Objective-C code can raise exceptions that Nimble will catch.

Collection Membership

// Swift

// Passes if all of the expected values are members of 'actual':
expect(actual).to(contain(expected...))

// Passes if 'actual' is empty (i.e. it contains no elements):
expect(actual).to(beEmpty())
// Objective-C

// Passes if expected is a member of 'actual':
expect(actual).to(contain(expected));

// Passes if 'actual' is empty (i.e. it contains no elements):
expect(actual).to(beEmpty());

In Swift contain takes any number of arguments. The expectation passes if all of them are members of the collection. In Objective-C, contain only takes one argument for now.

For example, to assert that a list of sea creature names contains "dolphin" and "starfish":

// Swift

expect(["whale", "dolphin", "starfish"]).to(contain("dolphin", "starfish"))
// Objective-C

expect(@[@"whale", @"dolphin", @"starfish"]).to(contain(@"dolphin"));
expect(@[@"whale", @"dolphin", @"starfish"]).to(contain(@"starfish"));

contain and beEmpty expect collections to be instances of NSArray, NSSet, or a Swift collection composed of Equatable elements.

To test whether a set of elements is present at the beginning or end of an ordered collection, use beginWith and endWith:

// Swift

// Passes if the elements in expected appear at the beginning of 'actual':
expect(actual).to(beginWith(expected...))

// Passes if the the elements in expected come at the end of 'actual':
expect(actual).to(endWith(expected...))
// Objective-C

// Passes if the elements in expected appear at the beginning of 'actual':
expect(actual).to(beginWith(expected));

// Passes if the the elements in expected come at the end of 'actual':
expect(actual).to(endWith(expected));

beginWith and endWith expect collections to be instances of NSArray, or ordered Swift collections composed of Equatable elements.

Like contain, in Objective-C beginWith and endWith only support a single argument for now.

For code that returns collections of complex objects without a strict ordering, there is the containElementSatisfying matcher:

// Swift

struct Turtle {
    let color: String
}

let turtles: [Turtle] = functionThatReturnsSomeTurtlesInAnyOrder()

// This set of matchers passes regardless of whether the array is 
// [{color: "blue"}, {color: "green"}] or [{color: "green"}, {color: "blue"}]:

expect(turtles).to(containElementSatisfying({ turtle in
    return turtle.color == "green"
}))
expect(turtles).to(containElementSatisfying({ turtle in
    return turtle.color == "blue"
}, "that is a turtle with color 'blue'"))

// The second matcher will incorporate the provided string in the error message
// should it fail
// Objective-C

@interface Turtle : NSObject
@property (nonatomic, readonly, nonnull) NSString *color;
@end

@implementation Turtle 
@end

NSArray<Turtle *> * __nonnull turtles = functionThatReturnsSomeTurtlesInAnyOrder();

// This set of matchers passes regardless of whether the array is 
// [{color: "blue"}, {color: "green"}] or [{color: "green"}, {color: "blue"}]:

expect(turtles).to(containElementSatisfying(^BOOL(id __nonnull object) {
    return [[turtle color] isEqualToString:@"green"];
}));
expect(turtles).to(containElementSatisfying(^BOOL(id __nonnull object) {
    return [[turtle color] isEqualToString:@"blue"];
}));

Strings

// Swift

// Passes if 'actual' contains 'substring':
expect(actual).to(contain(substring))

// Passes if 'actual' begins with 'prefix':
expect(actual).to(beginWith(prefix))

// Passes if 'actual' ends with 'suffix':
expect(actual).to(endWith(suffix))

// Passes if 'actual' represents the empty string, "":
expect(actual).to(beEmpty())

// Passes if 'actual' matches the regular expression defined in 'expected':
expect(actual).to(match(expected))
// Objective-C

// Passes if 'actual' contains 'substring':
expect(actual).to(contain(expected));

// Passes if 'actual' begins with 'prefix':
expect(actual).to(beginWith(prefix));

// Passes if 'actual' ends with 'suffix':
expect(actual).to(endWith(suffix));

// Passes if 'actual' represents the empty string, "":
expect(actual).to(beEmpty());

// Passes if 'actual' matches the regular expression defined in 'expected':
expect(actual).to(match(expected))

Collection Elements

Nimble provides a means to check that all elements of a collection pass a given expectation.

Swift

In Swift, the collection must be an instance of a type conforming to Sequence.

// Swift

// Providing a custom function:
expect([1, 2, 3, 4]).to(allPass { $0 < 5 })

// Composing the expectation with another matcher:
expect([1, 2, 3, 4]).to(allPass(beLessThan(5)))

Objective-C

In Objective-C, the collection must be an instance of a type which implements the NSFastEnumeration protocol, and whose elements are instances of a type which subclasses NSObject.

Additionally, unlike in Swift, there is no override to specify a custom matcher function.

// Objective-C

expect(@[@1, @2, @3, @4]).to(allPass(beLessThan(@5)));

Collection Count

// Swift

// Passes if 'actual' contains the 'expected' number of elements:
expect(actual).to(haveCount(expected))

// Passes if 'actual' does _not_ contain the 'expected' number of elements:
expect(actual).notTo(haveCount(expected))
// Objective-C

// Passes if 'actual' contains the 'expected' number of elements:
expect(actual).to(haveCount(expected))

// Passes if 'actual' does _not_ contain the 'expected' number of elements:
expect(actual).notTo(haveCount(expected))

For Swift, the actual value must be an instance of a type conforming to Collection. For example, instances of Array, Dictionary, or Set.

For Objective-C, the actual value must be one of the following classes, or their subclasses:

  • NSArray,
  • NSDictionary,
  • NSSet, or
  • NSHashTable.

Notifications

// Swift
let testNotification = Notification(name: Notification.Name("Foo"), object: nil)

// Passes if the closure in expect { ... } posts a notification to the default
// notification center.
expect {
    NotificationCenter.default.post(testNotification)
}.to(postNotifications(equal([testNotification])))

// Passes if the closure in expect { ... } posts a notification to a given
// notification center
let notificationCenter = NotificationCenter()
expect {
    notificationCenter.post(testNotification)
}.to(postNotifications(equal([testNotification]), from: notificationCenter))

// Passes if the closure in expect { ... } posts a notification with the provided names to a given
// notification center. Make sure to use this when running tests on Catalina, 
// using DistributedNotificationCenter as there is currently no way 
// of observing notifications without providing specific names.
let distributedNotificationCenter = DistributedNotificationCenter()
expect {
    distributedNotificationCenter.post(testNotification)
}.toEventually(postDistributedNotifications(equal([testNotification]),
                                  from: distributedNotificationCenter,
                                  names: [testNotification.name]))

This matcher is only available in Swift.

Result

// Swift
let aResult: Result<String, Error> = .success("Hooray") 

// passes if result is .success
expect(aResult).to(beSuccess()) 

// passes if result value is .success and validates Success value
expect(aResult).to(beSuccess { value in
    expect(value).to(equal("Hooray"))
})


enum AnError: Error {
    case somethingHappened
}
let otherResult: Result<String, AnError> = .failure(.somethingHappened) 

// passes if result is .failure
expect(otherResult).to(beFailure()) 

// passes if result value is .failure and validates error
expect(otherResult).to(beFailure { error in
    expect(error).to(matchError(AnError.somethingHappened))
}) 

This matcher is only available in Swift.

Matching a value to any of a group of matchers

// Swift

// passes if actual is either less than 10 or greater than 20
expect(actual).to(satisfyAnyOf(beLessThan(10), beGreaterThan(20)))

// can include any number of matchers -- the following will pass
// **be careful** -- too many matchers can be the sign of an unfocused test
expect(6).to(satisfyAnyOf(equal(2), equal(3), equal(4), equal(5), equal(6), equal(7)))

// in Swift you also have the option to use the || operator to achieve a similar function
expect(82).to(beLessThan(50) || beGreaterThan(80))
// Objective-C

// passes if actual is either less than 10 or greater than 20
expect(actual).to(satisfyAnyOf(beLessThan(@10), beGreaterThan(@20)))

// can include any number of matchers -- the following will pass
// **be careful** -- too many matchers can be the sign of an unfocused test
expect(@6).to(satisfyAnyOf(equal(@2), equal(@3), equal(@4), equal(@5), equal(@6), equal(@7)))

Note: This matcher allows you to chain any number of matchers together. This provides flexibility, but if you find yourself chaining many matchers together in one test, consider whether you could instead refactor that single test into multiple, more precisely focused tests for better coverage.

Custom Validation

// Swift

// passes if .succeeded is returned from the closure
expect {
    guard case .enumCaseWithAssociatedValueThatIDontCareAbout = actual else {
        return .failed(reason: "wrong enum case")
    }

    return .succeeded
}.to(succeed())

// passes if .failed is returned from the closure
expect {
    guard case .enumCaseWithAssociatedValueThatIDontCareAbout = actual else {
        return .failed(reason: "wrong enum case")
    }

    return .succeeded
}.notTo(succeed())

The String provided with .failed() is shown when the test fails.

When using toEventually() be careful not to make state changes or run process intensive code since this closure will be ran many times.

Writing Your Own Matchers

In Nimble, matchers are Swift functions that take an expected value and return a Predicate closure. Take equal, for example:

// Swift

public func equal<T: Equatable>(expectedValue: T?) -> Predicate<T> {
    // Can be shortened to:
    //   Predicate { actual in  ... }
    //
    // But shown with types here for clarity.
    return Predicate { (actualExpression: Expression<T>) throws -> PredicateResult in
        let msg = ExpectationMessage.expectedActualValueTo("equal <\(expectedValue)>")
        if let actualValue = try actualExpression.evaluate() {
            return PredicateResult(
                bool: actualValue == expectedValue!,
                message: msg
            )
        } else {
            return PredicateResult(
                status: .fail,
                message: msg.appendedBeNilHint()
            )
        }
    }
}

The return value of a Predicate closure is a PredicateResult that indicates whether the actual value matches the expectation and what error message to display on failure.

The actual equal matcher function does not match when expected are nil; the example above has been edited for brevity.

Since matchers are just Swift functions, you can define them anywhere: at the top of your test file, in a file shared by all of your tests, or in an Xcode project you distribute to others.

If you write a matcher you think everyone can use, consider adding it to Nimble's built-in set of matchers by sending a pull request! Or distribute it yourself via GitHub.

For examples of how to write your own matchers, just check out the Matchers directory to see how Nimble's built-in set of matchers are implemented. You can also check out the tips below.

PredicateResult

PredicateResult is the return struct that Predicate return to indicate success and failure. A PredicateResult is made up of two values: PredicateStatus and ExpectationMessage.

Instead of a boolean, PredicateStatus captures a trinary set of values:

// Swift

public enum PredicateStatus {
// The predicate "passes" with the given expression
// eg - expect(1).to(equal(1))
case matches

// The predicate "fails" with the given expression
// eg - expect(1).toNot(equal(1))
case doesNotMatch

// The predicate never "passes" with the given expression, even if negated
// eg - expect(nil as Int?).toNot(equal(1))
case fail

// ...
}

Meanwhile, ExpectationMessage provides messaging semantics for error reporting.

// Swift

public indirect enum ExpectationMessage {
// Emits standard error message:
// eg - "expected to <string>, got <actual>"
case expectedActualValueTo(/* message: */ String)

// Allows any free-form message
// eg - "<string>"
case fail(/* message: */ String)

// ...
}

Predicates should usually depend on either .expectedActualValueTo(..) or .fail(..) when reporting errors. Special cases can be used for the other enum cases.

Finally, if your Predicate utilizes other Predicates, you can utilize .appended(details:) and .appended(message:) methods to annotate an existing error with more details.

A common message to append is failing on nils. For that, .appendedBeNilHint() can be used.

Lazy Evaluation

actualExpression is a lazy, memoized closure around the value provided to the expect function. The expression can either be a closure or a value directly passed to expect(...). In order to determine whether that value matches, custom matchers should call actualExpression.evaluate():

// Swift

public func beNil<T>() -> Predicate<T> {
    // Predicate.simpleNilable(..) automatically generates ExpectationMessage for
    // us based on the string we provide to it. Also, the 'Nilable' postfix indicates
    // that this Predicate supports matching against nil actualExpressions, instead of
    // always resulting in a PredicateStatus.fail result -- which is true for
    // Predicate.simple(..)
    return Predicate.simpleNilable("be nil") { actualExpression in
        let actualValue = try actualExpression.evaluate()
        return PredicateStatus(bool: actualValue == nil)
    }
}

In the above example, actualExpression is not nil -- it is a closure that returns a value. The value it returns, which is accessed via the evaluate() method, may be nil. If that value is nil, the beNil matcher function returns true, indicating that the expectation passed.

Type Checking via Swift Generics

Using Swift's generics, matchers can constrain the type of the actual value passed to the expect function by modifying the return type.

For example, the following matcher, haveDescription, only accepts actual values that implement the Printable protocol. It checks their description against the one provided to the matcher function, and passes if they are the same:

// Swift

public func haveDescription(description: String) -> Predicate<Printable?> {
    return Predicate.simple("have description") { actual in
        return PredicateStatus(bool: actual.evaluate().description == description)
    }
}

Customizing Failure Messages

When using Predicate.simple(..) or Predicate.simpleNilable(..), Nimble outputs the following failure message when an expectation fails:

// where `message` is the first string argument and
// `actual` is the actual value received in `expect(..)`
"expected to \(message), got <\(actual)>"

You can customize this message by modifying the way you create a Predicate.

Basic Customization

For slightly more complex error messaging, receive the created failure message with Predicate.define(..):

// Swift

public func equal<T: Equatable>(_ expectedValue: T?) -> Predicate<T> {
    return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg 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(bool: matches, message: msg)
    }
}

In the example above, msg is defined based on the string given to Predicate.define. The code looks akin to:

// Swift

let msg = ExpectationMessage.expectedActualValueTo("equal <\(stringify(expectedValue))>")

Full Customization

To fully customize the behavior of the Predicate, use the overload that expects a PredicateResult to be returned.

Along with PredicateResult, there are other ExpectationMessage enum values you can use:

public indirect enum ExpectationMessage {
// Emits standard error message:
// eg - "expected to <message>, got <actual>"
case expectedActualValueTo(/* message: */ String)

// Allows any free-form message
// eg - "<message>"
case fail(/* message: */ String)

// Emits standard error message with a custom actual value instead of the default.
// eg - "expected to <message>, got <actual>"
case expectedCustomValueTo(/* message: */ String, /* actual: */ String)

// Emits standard error message without mentioning the actual value
// eg - "expected to <message>"
case expectedTo(/* message: */ String)

// ...
}

For matchers that compose other matchers, there are a handful of helper functions to annotate messages.

appended(message: String) is used to append to the original failure message:

// produces "expected to be true, got <actual> (use beFalse() for inverse)"
// appended message do show up inline in Xcode.
.expectedActualValueTo("be true").appended(message: " (use beFalse() for inverse)")

For a more comprehensive message that spans multiple lines, use appended(details: String) instead:

// produces "expected to be true, got <actual>\n\nuse beFalse() for inverse\nor use beNil()"
// details do not show inline in Xcode, but do show up in test logs.
.expectedActualValueTo("be true").appended(details: "use beFalse() for inverse\nor use beNil()")

Supporting Objective-C

To use a custom matcher written in Swift from Objective-C, you'll have to extend the NMBPredicate class, adding a new class method for your custom matcher. The example below defines the class method +[NMBPredicate beNilMatcher]:

// Swift

extension NMBPredicate {
    @objc public class func beNilMatcher() -> NMBPredicate {
        return NMBPredicate { actualExpression in
            return try beNil().satisfies(actualExpression).toObjectiveC()
        }
    }
}

The above allows you to use the matcher from Objective-C:

// Objective-C

expect(actual).to([NMBPredicate beNilMatcher]());

To make the syntax easier to use, define a C function that calls the class method:

// Objective-C

FOUNDATION_EXPORT NMBPredicate *beNil() {
    return [NMBPredicate beNilMatcher];
}

Properly Handling nil in Objective-C Matchers

When supporting Objective-C, make sure you handle nil appropriately. Like Cedar, most matchers do not match with nil. This is to bring prevent test writers from being surprised by nil values where they did not expect them.

Nimble provides the beNil matcher function for test writer that want to make expectations on nil objects:

// Objective-C

expect(nil).to(equal(nil)); // fails
expect(nil).to(beNil());    // passes

If your matcher does not want to match with nil, you use Predicate.define or Predicate.simple. Using those factory methods will automatically generate expected value failure messages when they're nil.

public func beginWith<S: Sequence>(_ startingElement: S.Element) -> Predicate<S> where S.Element: Equatable {
    return Predicate.simple("begin with <\(startingElement)>") { actualExpression in
        guard let actualValue = try actualExpression.evaluate() else { return .fail }

        var actualGenerator = actualValue.makeIterator()
        return PredicateStatus(bool: actualGenerator.next() == startingElement)
    }
}

extension NMBPredicate {
    @objc public class func beginWithMatcher(_ expected: Any) -> NMBPredicate {
        return NMBPredicate { actualExpression in
            let actual = try actualExpression.evaluate()
            let expr = actualExpression.cast { $0 as? NMBOrderedCollection }
            return try beginWith(expected).satisfies(expr).toObjectiveC()
        }
    }
}

Migrating from the Old Matcher API

Previously (<7.0.0), Nimble supported matchers via the following types:

  • Matcher
  • NonNilMatcherFunc
  • MatcherFunc

All of those types have been replaced by Predicate. The old API has been removed completely in Nimble v10.

Installing Nimble

Nimble can be used on its own, or in conjunction with its sister project, Quick. To install both Quick and Nimble, follow the installation instructions in the Quick Documentation.

Nimble can currently be installed in one of two ways: using CocoaPods, or with git submodules.

Installing Nimble as a Submodule

To use Nimble as a submodule to test your macOS, iOS or tvOS applications, follow these 4 easy steps:

  1. Clone the Nimble repository
  2. Add Nimble.xcodeproj to the Xcode workspace for your project
  3. Link Nimble.framework to your test target
  4. Start writing expectations!

For more detailed instructions on each of these steps, read How to Install Quick. Ignore the steps involving adding Quick to your project in order to install just Nimble.

Installing Nimble via CocoaPods

To use Nimble in CocoaPods to test your macOS, iOS or tvOS applications, add Nimble to your podfile and add the use_frameworks! line to enable Swift support for CocoaPods.

platform :ios, '8.0'

source 'https://github.com/CocoaPods/Specs.git'

# Whatever pods you need for your app go here

target 'YOUR_APP_NAME_HERE_Tests', :exclusive => true do
  use_frameworks!
  pod 'Nimble'
end

Finally run pod install.

Installing Nimble via Accio

Add the following to your Package.swift:

.package(url: "https://github.com/Quick/Nimble.git", .upToNextMajor(from: "8.0.1")),

Next, add Nimble to your App targets dependencies like so:

.testTarget(
    name: "AppTests",
    dependencies: [
        "Nimble",
    ]
),

Then run accio update.

Using Nimble without XCTest

Nimble is integrated with XCTest to allow it work well when used in Xcode test bundles, however it can also be used in a standalone app. After installing Nimble using one of the above methods, there are two additional steps required to make this work.

  1. Create a custom assertion handler and assign an instance of it to the global NimbleAssertionHandler variable. For example:
class MyAssertionHandler : AssertionHandler {
    func assert(assertion: Bool, message: FailureMessage, location: SourceLocation) {
        if (!assertion) {
            print("Expectation failed: \(message.stringValue)")
        }
    }
}
// Somewhere before you use any assertions
NimbleAssertionHandler = MyAssertionHandler()
  1. Add a post-build action to fix an issue with the Swift XCTest support library being unnecessarily copied into your app
  • Edit your scheme in Xcode, and navigate to Build -> Post-actions
  • Click the "+" icon and select "New Run Script Action"
  • Open the "Provide build settings from" dropdown and select your target
  • Enter the following script contents:
rm "${SWIFT_STDLIB_TOOL_DESTINATION_DIR}/libswiftXCTest.dylib"

You can now use Nimble assertions in your code and handle failures as you see fit.

Comments
  • [WIP] Swift 3.0 Compatibility

    [WIP] Swift 3.0 Compatibility

    • Uses wrapper that converting from Swift 3.0 API to Swift 2.2 API.
    • Includes #261
    • Uses swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a
    • Passes swift test on OS X and Linux
    • [x] Update test script for Linux
    opened by norio-nomura 38
  • Call matcher

    Call matcher

    Something I've been working on to give swift the ability to test whether a dependency had a function called on it. Something I've grown accustomed to having while using Cedar.

    After trying to use SwiftyMock with Quick and Nimble I noticed some bizarre behavior so I set out to make a much lighter version of function recording and thought it would be cool if this was already built into Nimble.

    Please let me know what you think. Feel free to change the README.md changes. That was just a first stab at describing what it does and how to use it.

    -Brian Radebaugh

    opened by Rivukis 28
  • Add matcher to check whether an assertion was failed

    Add matcher to check whether an assertion was failed

    I added a throwAssertion() matcher, which returns true when a precondition fails or fatalError() is called (Issue #234). This currently only supports x86_64, and not the tvOS simulator.

    Some open questions:

    • [ ] ~~Fix Circle build issues~~
    • [x] How do we want to credit @mattgallagher, as discussed in the issue thread?
    • [x] Are we okay shipping with tvOS support based on POSIX signals? This means that tvOS schemes will have to turn off the Debug executable setting in their Test configuration. @mattgallagher and I discussed this in Issue #3 of his repo
    • [x] How do we handle non-x86_64 test runs? I currently have the throwAssertion() matcher conditionally compiling to fatally error with a clear message. Would it be better to return false from the matcher with a descriptive message?
    • [x] Are we happy with the throwAssertion name? If not, what would be better?
    • [x] I made CwlPreconditionTesting a submodule, and then included the files from it statically. Is this the right way to go?
    • [x] Update README to document throwAssertion() (or whatever it ends up being called)
    opened by abbeycode 27
  • Xcode 12 beta 5 and 6 build error `ambiguous use of 'expect'`

    Xcode 12 beta 5 and 6 build error `ambiguous use of 'expect'`

    • [x] I have read CONTRIBUTING and have done my best to follow them.

    What did you do?

    I installed Xcode 12 beta 5 and 6 and tried to build our existing (test) code:

    it("should throw an error") {
        expect {
            try JSONDecoder().decode(SomeModel.self, from: data)
        }.to(throwError())
    }
    

    What did you expect to happen?

    I expected the code to compile.

    What actually happened instead?

    The code failed to compile with a ambiguous use of 'expect' error. With these two candidates:

    • Sources/Nimble/DSL.swift#L1-L8
      • public func expect<T>(_ expression: @autoclosure @escaping () throws -> T?, file: FileString = #file, line: UInt = #line) -> Expectation<T>
    • Sources/Nimble/DSL.swift#L10-L17: Found this candidate
      • public func expect<T>(_ file: FileString = #file, line: UInt = #line, expression: @escaping () throws -> T?) -> Expectation<T>

    Environment

    List the software versions you're using:

    • Quick: 2.2.1 + 3.0.0
    • Nimble: 8.1.1 + 9.0.0-rc.1
    • Xcode Version: 12.0 beta 5 (12A8189h), 12.0 beta 6 (12A8189n)
    • Swift Version: Xcode Default

    Please also mention which package manager you used and its version. Delete the other package managers in this list:

    • Carthage: 0.35.0

    Project that demonstrates the issue

    https://github.com/rastersize/Xcode12Beta5Nimble

    After cloning do the following:

    1. Run carthage.sh bootstrap
      • The script works around a Carthage bug with Xcode 12 beta 4+ and universal binaries. As newer versions of Xcode 12 will produce an arm64 slice for both the simulator and device, and a universal binary can’t contain two slices for the same architecture.
    2. Open the Xcode project
    3. Build for testing

    Workaround

    Change the call-site of expect to specify using the non-autoclosure function. That is:

    it("should throw an error") {
        expect(expression: {
            try JSONDecoder().decode(SomeModel.self, from: data)
        }).to(throwError())
    }
    

    Not as pretty but it works for now.

    opened by rastersize 22
  • Add NSNotification testing features

    Add NSNotification testing features

    • [x] Test 1+ posted notification matches success
    • [x] Test 0 posted notification success
    • [x] Test notification name match failure
    • [x] Test notification object match failure
    • [x] Test eventual notification match
    • [ ] Add ObjC support
    • [ ] Update README

    I was writing some tests for an object that uses NSNotification to broadcast will/did state change events globally. To test this, I needed to verify whether a notification was or wasn't posted. This was partially doable with XCTest's expectationForNotification, but I figured it would be cooler—I mean more expressive/readable 🤓—to use a Nimble matcher.

    opened by bgerstle 21
  • Compile under Xcode 8 beta 1

    Compile under Xcode 8 beta 1

    Sets "Use Legacy Swift Language Version" to YES, i.e., SWIFT_VERSION = 2.3.

    This change alone causes the following linker error:

    duplicate symbol __non_lazy_classes in:
        /Applications/Xcode-beta.app/Contents/Developer/Toolchains/Swift_2.3.xctoolchain/usr/lib/arc/libarclite_macosx.a(arclite.o)
        /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_macosx.a(arclite.o)
    duplicate symbol _OBJC_METACLASS_$___ARCLite__ in:
        /Applications/Xcode-beta.app/Contents/Developer/Toolchains/Swift_2.3.xctoolchain/usr/lib/arc/libarclite_macosx.a(arclite.o)
        /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_macosx.a(arclite.o)
    ld: 2 duplicate symbols for architecture x86_64
    

    This appears to be caused by the "Implicitly Link Objective-C Runtime Support" build setting (a.k.a. CLANG_LINK_OBJC_RUNTIME). Disabling just this setting doesn't resolve the error, but disabling the project-level ARC setting does resolve the linker error. As a result the -fobjc-arc compiler flag has to be set on each Objective-C file to ensure it is compiled with ARC enabled.

    opened by sharplet 20
  • Crash using Nimble 5.0.0 & OCMock 3.3.1

    Crash using Nimble 5.0.0 & OCMock 3.3.1

    Migrating https://github.com/erikdoe/ocmock/issues/319 here. I couldn't quite figure it out, but it seems like Nimble was failing to print a description of an OCClassMockObject.

    bug 
    opened by modocache 18
  • Stall on main thread error

    Stall on main thread error

    Hi guys, I'm seeing this error with some fairly vanilla tests when run on a Bot on our Xcode Server. It only happens for tests which use the waitUntil function, testing some functions that use AlamoFire and checking the right data is returned in the callback. I'm stubbing the requests with Nocilla, but the issue still occurs if I don't stub and increase timeouts to accomodate for network latency.

    Strangely, it only crops up when the tests are run by an Xcode Bot. If I run the tests manually in Xcode on the Xcode Server and of course on my MacbookPro they run fine.

    I have tried using NSRunLoop.mainRunLoop().runUntilDate(NSDate()) just before the waitUntil as mentioned in #122 but this doesn't entirely fix the problem. I have to also increase the timeouts of my waitUntils to 10+ seconds, but even then some tests fail with the stall error. The file of 20 tests normally executes in about 2 seconds or less normally.

    OSX Server 10.11.1, Xcode Server 5.0.15, Xcode 7.1. Running on iPhone 6 iOS9.1 simulator. Installed as a git submodule on at 2.0.0-rc.3. But we use cocoapods for other dependencies (0.38.2)

    bug tricky 
    opened by mfclarke 18
  • Make Async Expectations more deterministic

    Make Async Expectations more deterministic

    Nimble has flaky tests on slower machines :cry:.

    It's related to the pollBlock abort feature when the main thread is backlogged with deferred messages that take a while to compute. This causes NSRunLoop.runUntilDate to run longer than expected. Nimble currently tries to stop the runloop if it's taking too long from another thread.

    This sometimes occurs on travis but can be reliably reproduced on a mac mini that runs Xcode Server + all simulator devices.

    This causes failures in AsynchronousTest.swift:

    screen shot 2015-08-30 at 7 29 28 pm

    screen shot 2015-08-30 at 7 29 23 pm

    These are two separate builds on Xcode Server reports as failed. Most Xcode Server builds tend to alternate between these two sets of failures.

    More investigation is required.

    bug help wanted high priority tricky 
    opened by jeffh 18
  • Weaken the dependency on XCTest

    Weaken the dependency on XCTest

    I had some good success working on what seems like a great solution to #144. The weak linking seems to be working successfully. The process dies with a dylib error the instant that you try to instantiate NimbleXCTestHandler without the XCTest framework+lib around, so I added some code to prevent that form happening. We could potentially try to add a non-crashing fallback assertion handler for this situation as well.

    Thoughts? @jeffh @tjarratt

    high priority 
    opened by briancroom 18
  • Add public accessors for types to begin supporting XCode 6 beta 4.

    Add public accessors for types to begin supporting XCode 6 beta 4.

    This is the first step in getting Nimble ready for XCode 6 beta 4.

    The following things are included in this:

    • public accessors added
    • removed the bridging header (not supported in Frameworks)
    • forward declared NMBExpectation (was getting an error related to it)

    I'm still getting an error related to:

    Undefined symbols for architecture x86_64:
      "_OBJC_CLASS_$_NMBExpectation", referenced from:
          objc-class-ref in DSL-B83371164C24FC7F.o
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    

    But I'm unfamiliar with what the problem is here.

    I thought this might help in terms of getting a start, though. I'll continue to try to resolve the "undefined symbols" error as well.

    opened by drmohundro 18
  • Spurious failure: Nested async expectations are not allowed to avoid creating flaky tests

    Spurious failure: Nested async expectations are not allowed to avoid creating flaky tests

    • [x] I have read CONTRIBUTING and have done my best to follow them.

    What did you do?

    I ran a test that has passed many times in the past, but is now failing.

    class ExportDataActionSpec: QuickSpec {
        override func spec() {
            describe("ExportDataAction") {
                it("should do something") {
                    let viewController = UIViewControllerMock()
                    let session = MockNetworkSession.session
                    let action = ExportDataAction(viewController: viewController, session: session)
                    action.execute()
                    expect(viewController.viewControllerFromPresentCall).toEventually(beAKindOf(UIActivityViewController.self), timeout: .seconds(3))
                }
            }
        }
    }
    

    What did you expect to happen?

    I expect the test to pass.

    The test always passes locally, but only seems to fail when running in CI. (github action)

    What actually happened instead?

    The test failed with:

    Nested async expectations are not allowed to avoid creating flaky tests.
    

    I don't understand this message as there are no nested async expectations. What am I missing?

    Environment

    List the software versions you're using:

    • Quick: 4.0.0
    • Nimble: 9.2.1 (also tried 11.2.1 and got the same error)
    • Xcode Version: 14.0 (14A309)
    • Swift Version: Xcode Default

    Please also mention which package manager you used and its version. Delete the other package managers in this list:

    • Swift Package Manager 5.7.0
    opened by xinsight 0
  • Rename AsyncDefaults to PollingDefaults

    Rename AsyncDefaults to PollingDefaults

    Forgot about this until I was writing the documentation about configuring AsyncDefaults.

    Given the overall rename of the toEventually and waitUntil version of async to the more accurate "polling", it makes logical sense to rename the AsyncDefaults closure to PollingDefaults.

    This is implemented to be just a deprecation warning with a replaced. The functionality is still the same. My idea is to keep the AsyncDefaults struct around for a bit longer. After Nimble 12 is released, I'll submit a PR changing the deprecation warning to a removed. After Nimble 13, I'll fully remove AsyncDefaults.

    This feels like a more-than-fair enough offramp.

    opened by younata 0
  • Unable to verify that an async function eventually sends an NSNotification.

    Unable to verify that an async function eventually sends an NSNotification.

    The following test does not work:

    func asyncFunctionPostingNotification() async {
        // ...
        NotificationCenter.default.post(...)
    }
    
    expect { await asyncFunctionPostingNotification() }.to(postNotification(...))
    

    Because of how async expectations are implemented, the Expression (await asyncFunctionPostingNotification() } is called before the matcher (postNotification(...)) is called. This is a result of the approach we take with async expressions, where the expression is evaluated, then the result of the expression is passed into the matcher.

    Perhaps we should take this as a chance to rethink how AsyncExpectation works, and provide full end-to-end support for async expressions. We could use something approaching the current method as a fallback for existing, non-async matchers.

    bug 
    opened by younata 0
  • Objective-C support in the Swift Package version

    Objective-C support in the Swift Package version

    Resolves https://github.com/Quick/Nimble/issues/989

    • Adds Objective-C support for the SwiftPM version of Nimble

    There's a lot of follow-on, entirely stemming from the fact that SwiftPM doesn't support mixed-language targets.

    • Creates a NimbleSharedTestHelpers internal framework, used only for testing Swift and Objective-C. This should not be exported or used by others.
    • Separates the old Tests/NimbleTests/objc folder out into Tests/NimbleObjectiveCTests/
    • Converts a lot of #if SWIFT_PACKAGE to #if canImport(Darwin) (or with the equivalent !'ed versions), because now we do support Objective-C in SwiftPM.
    • SwiftPM users w/ objc tests will have to use both @import Nimble; and @import NimbleObjectiveC;

    Per the tests, this doesn't appear to support capturing objc exceptions. Failure messages will be slightly different for the SwiftPM version of objc.

    Todo

    • [ ] Gather feedback on if this actually solves the issue.
    • [ ] Write documentation on how to use this.
    opened by younata 0
  • AsyncTests in watchOS are buggy.

    AsyncTests in watchOS are buggy.

    Seems that the runloop control isn't quite where we want it?

    Also seems to mostly be a CI thing? Like, I can't easily replicate this on my personal machine, but if you look at CI runs, it's at least 25% or so of runs have test failures running the async tests on watchOS.

    opened by younata 1
  • Support Using Nimble without XCTest.

    Support Using Nimble without XCTest.

    Pulled out of https://github.com/Quick/Nimble/discussions/986.

    We have documentation for how to do this, but it's either outdated or flat-out wrong.

    This ticket is to cover figuring out how to run nimble without xctest, updating our documentation, and adding verification so that we don't regress on this.

    enhancement 
    opened by younata 0
Releases(v11.2.1)
  • v11.2.1(Nov 18, 2022)

    Fixed tripping the main thread checker in async toEventually checks. Your CI should no longer report that tests erroneously crashed because the expression's debugDescription dared to reference something that needed to run on the main thread.

    What's Changed

    • Add documentation on recommended ways to configure AsyncDefaults by @younata in https://github.com/Quick/Nimble/pull/1022
    • Ensure that stringify'ing an expression as part of the async/await polling infrastructure always happens on the main thread by @younata in https://github.com/Quick/Nimble/pull/1024

    Full Changelog: https://github.com/Quick/Nimble/compare/v11.2.0...v11.2.1

    Source code(tar.gz)
    Source code(zip)
  • v11.2.0(Nov 12, 2022)

    Improved developer experience by allowing you to use the sync form of expect in a test that has other usage of async test. i.e. the following code compiles again:

    class MyTest: XCTestCase {
        func testExample() {
            await someAsyncFunction()
            expect(someValue).to(equal(expectedValue))
        }
    }
    

    What's Changed

    • Remove autoclosure tag with async expectations by @younata in https://github.com/Quick/Nimble/pull/1020

    Full Changelog: https://github.com/Quick/Nimble/compare/v11.1.1...v11.2.0

    Source code(tar.gz)
    Source code(zip)
  • v11.1.1(Nov 11, 2022)

    What's Changed

    • Fix regression where named tuples could not be compared with unnamed tuples of the same types using the == operator by @younata in https://github.com/Quick/Nimble/pull/1017
    • Use uncached expression in the async versions of toEventually by @younata in https://github.com/Quick/Nimble/pull/1018

    Full Changelog: https://github.com/Quick/Nimble/compare/v11.1.0...v11.1.1

    Source code(tar.gz)
    Source code(zip)
  • v11.1.0(Nov 2, 2022)

    11.1.0 drops support for Swift 5.6 (you must use Xcode 14 or later). https://github.com/Quick/Nimble/pull/1009

    11.1.0 fixes a developer experience bug where you could unknowingly use the sync version of toEventually in an async context, which will cause test timeout failures. https://github.com/Quick/Nimble/pull/1010

    That is, the following test method (XCTest-style) would compile with no errors or warnings emitted, but fail at test runtime due to timeout issues. In v11.1.0, this now emits a warning that you're using the wrong version of toEventually (and similar).

    @MainActor func testSomething() async {
        expect(1).toEventually(equal(1)) // (in v11.0.0, this would not emit any kind of warning or error, but would definitely fail with a timeout error)
    }
    

    Full Changelog: https://github.com/Quick/Nimble/compare/v11.0.0...v11.1.0

    Source code(tar.gz)
    Source code(zip)
  • v11.0.0(Oct 31, 2022)

    This closes the v11.0.0 milestone.

    Highlights

    Primarily, this release now supports running tests in async contexts.

    Fixed

    • toEventually et. al. now works from background threads. https://github.com/Quick/Nimble/pull/1000
    • toEventually et. al. now work in async tests. https://github.com/Quick/Nimble/issues/1007

    New

    • Async/await support in expectation expressions (e.g. await expect(await someAsyncFunction()).to(...)). https://github.com/Quick/Nimble/pull/1004
    • append(details:) now respects whitespace that is in the message. https://github.com/Quick/Nimble/pull/1001
    • watchOS support. https://github.com/Quick/Nimble/pull/916
    • You can now directly check if an expectation has passed or not. https://github.com/Quick/Nimble/pull/995

    Breaking

    • Raised version requirements to Swift 5.6, iOS 13, macOS 10.15, tvOS 13, and watchOS 6. https://github.com/Quick/Nimble/issues/984
    • The Expectation struct is now a protocol. There are 2 concrete implementations, SyncExpectation and AsyncExpectation. AsyncExpectation does not support toEventually, and is meant for awaiting on async functions. SyncExpectation is effectively the older Expectation implementation, and works as it used to. https://github.com/Quick/Nimble/pull/1004

    Auto-generated release notes

    What's Changed

    • Add support for watchOS by @JosephDuffy in https://github.com/Quick/Nimble/pull/916
    • Add @younata to funding.yml by @younata in https://github.com/Quick/Nimble/pull/994
    • Expose whether an expectation has passed and provide an option to throw by @bnickel in https://github.com/Quick/Nimble/pull/995
    • Raise minimum required OS and Swift Versions to Async-Support versions. by @younata in https://github.com/Quick/Nimble/pull/999
    • Don't strip whitespace from appended newlines by @younata in https://github.com/Quick/Nimble/pull/1001
    • Allow toEventually to run on background threads by @younata in https://github.com/Quick/Nimble/pull/1000
    • Allow using async/await in expect by @younata in https://github.com/Quick/Nimble/pull/1004
    • Get toEventually et al working in async contexts. by @younata in https://github.com/Quick/Nimble/pull/1007

    New Contributors

    • @JosephDuffy made their first contribution in https://github.com/Quick/Nimble/pull/916
    • @bnickel made their first contribution in https://github.com/Quick/Nimble/pull/995

    Full Changelog: https://github.com/Quick/Nimble/compare/v10.0.0...v11.0.0

    Source code(tar.gz)
    Source code(zip)
  • v10.0.0(Apr 27, 2022)

    This closes the v10.0.0 milestone.

    Highlights

    See additional details under the auto-generated release notes below.

    Fixed

    • Various documentation improvements
    • Remove various deprecated APIs

    New

    • Add toAlways, the inverse of toNever #969
    • Add beWithin matcher #886
    • Support nested optionals in beNil matcher #894
    • Support operator overloads for beNil matcher #893
    • Support throwAssertion matcher on SwiftPM on Linux #891
    • Add support for tuples of up to 6 elements to equal matcher, as with the standard library #880
    • Add matchers for Swift 5 Result type #643

    Breaking

    • Swift 5.4+
    • Xcode 12.5+
    • Remove old Matcher API #876
    • Use FloatingPoint protocol in beCloseTo matcher #879
    • passFunc of allPass matcher now takes S.Element over S.Element? #895

    Auto-generated release notes

    What's Changed

    • [BREAKING] Bump Swift requirement to 5.3 (Xcode 12) by @ikesyo in https://github.com/Quick/Nimble/pull/875
    • [README] Use expect with trailing closure syntax for the succeed matcher by @ikesyo in https://github.com/Quick/Nimble/pull/877
    • Suppress SwiftPM build warnings by @ikesyo in https://github.com/Quick/Nimble/pull/878
    • [BREAKING] Remove old Matcher API by @ikesyo in https://github.com/Quick/Nimble/pull/876
    • Add support for tuples of up to 6 elements to equal matcher, as with the standard library by @ikesyo in https://github.com/Quick/Nimble/pull/880
    • [BREAKING] Use FloatingPoint protocol in beCloseTo matcher by @ikesyo in https://github.com/Quick/Nimble/pull/879
    • Adding toNever expectation by @rkreutz in https://github.com/Quick/Nimble/pull/800
    • [ObjC] Add toNever expectation by @ikesyo in https://github.com/Quick/Nimble/pull/885
    • [BREAKING] Remove deprecated and unavailable APIs by @ikesyo in https://github.com/Quick/Nimble/pull/887
    • Add beWithin matcher by @ikesyo in https://github.com/Quick/Nimble/pull/886
    • Experimental SwiftWasm support by @ikesyo in https://github.com/Quick/Nimble/pull/889
    • Removed invalid anchors in the README. by @daveverwer in https://github.com/Quick/Nimble/pull/890
    • Support throwAssertion matcher on SwiftPM on Linux by @ikesyo in https://github.com/Quick/Nimble/pull/891
    • Adds matchers for Swift 5 Result type by @xzeror in https://github.com/Quick/Nimble/pull/643
    • Support operator overloads for beNil matcher by @ikesyo in https://github.com/Quick/Nimble/pull/893
    • Support nested optionals in beNil matcher by @ikesyo in https://github.com/Quick/Nimble/pull/894
    • [BREAKING] passFunc of allPass matcher now takes S.Element over S.Element? by @ikesyo in https://github.com/Quick/Nimble/pull/895
    • Follow master-to-main branch renaming by @ikesyo in https://github.com/Quick/Nimble/pull/896
    • Add more specific operator overloads for equal matcher by @ikesyo in https://github.com/Quick/Nimble/pull/897
    • Simplify equal matcher implementation by @ikesyo in https://github.com/Quick/Nimble/pull/907
    • [CI] Test Swift 5.5 Development by @ikesyo in https://github.com/Quick/Nimble/pull/908
    • [README] Remove deprecated Accio references by @ikesyo in https://github.com/Quick/Nimble/pull/909
    • [CI] macOS 11 and Xcode 12.5 by @ikesyo in https://github.com/Quick/Nimble/pull/912
    • Work around Xcode 13 archiving issue by @ikesyo in https://github.com/Quick/Nimble/pull/915
    • Bump addressable from 2.7.0 to 2.8.0 by @dependabot in https://github.com/Quick/Nimble/pull/918
    • Bump cocoapods from 1.10.1 to 1.10.2 by @dependabot in https://github.com/Quick/Nimble/pull/920
    • [CI] Test Xcode 13 by @ikesyo in https://github.com/Quick/Nimble/pull/926
    • Update CwlPreconditionTesting to 2.0.1 by @ikesyo in https://github.com/Quick/Nimble/pull/927
    • Reduce CI duration by removing tests on iPad simulator by @ikesyo in https://github.com/Quick/Nimble/pull/929
    • Specify header_dir in the podspec by @tsapeta in https://github.com/Quick/Nimble/pull/921
    • [CI] Disable swift:nightly tests temporarily by @ikesyo in https://github.com/Quick/Nimble/pull/930
    • Bump cocoapods from 1.10.2 to 1.11.0 by @dependabot in https://github.com/Quick/Nimble/pull/933
    • Bump swiftwasm/swiftwasm-action from 5.3 to 5.4 by @dependabot in https://github.com/Quick/Nimble/pull/932
    • Bump cocoapods from 1.11.0 to 1.11.1 by @dependabot in https://github.com/Quick/Nimble/pull/934
    • Bump cocoapods from 1.11.1 to 1.11.2 by @dependabot in https://github.com/Quick/Nimble/pull/935
    • [CI] Test Swift 5.5 stable by @ikesyo in https://github.com/Quick/Nimble/pull/939
    • Fix type inference problem for equal matchers on Xcode 12.5 or above by @ikesyo in https://github.com/Quick/Nimble/pull/938
    • [BREAKING] Bump Swift requirement to 5.4 (Xcode 12.5) by @ikesyo in https://github.com/Quick/Nimble/pull/941
    • Add support for precondition testing on Apple Silicon by @svenmuennich in https://github.com/Quick/Nimble/pull/948
    • beIdenticalTo / ===: disallow comparing non-objects by @NachoSoto in https://github.com/Quick/Nimble/pull/955
    • Bump actions/checkout from 2 to 3 by @dependabot in https://github.com/Quick/Nimble/pull/970
    • Fix succeed matcher type inference issue by @ikesyo in https://github.com/Quick/Nimble/pull/965
    • Bump cocoapods from 1.11.2 to 1.11.3 by @dependabot in https://github.com/Quick/Nimble/pull/972
    • Bump cocoapods-downloader from 1.5.1 to 1.6.3 by @dependabot in https://github.com/Quick/Nimble/pull/975
    • Disable ThrowAssertion for watchOS to make Nimble compile for watchOS by @danielsaidi in https://github.com/Quick/Nimble/pull/953
    • Add toAlways, the inverse of toNever. by @younata in https://github.com/Quick/Nimble/pull/969
    • Simplify the implementations of beTruthy and beFalsy by @younata in https://github.com/Quick/Nimble/pull/978
    • Fix equal compilation optionals by @younata in https://github.com/Quick/Nimble/pull/982
    • Add document for running tests on linux using docker by @younata in https://github.com/Quick/Nimble/pull/980
    • Update documentation for the beCloseTo and beWithin matchers by @younata in https://github.com/Quick/Nimble/pull/983
    • [10.0.0] bump version, gen docs by @jessesquires in https://github.com/Quick/Nimble/pull/985

    New Contributors

    • @rkreutz made their first contribution in https://github.com/Quick/Nimble/pull/800
    • @daveverwer made their first contribution in https://github.com/Quick/Nimble/pull/890
    • @xzeror made their first contribution in https://github.com/Quick/Nimble/pull/643
    • @tsapeta made their first contribution in https://github.com/Quick/Nimble/pull/921
    • @svenmuennich made their first contribution in https://github.com/Quick/Nimble/pull/948
    • @NachoSoto made their first contribution in https://github.com/Quick/Nimble/pull/955
    • @danielsaidi made their first contribution in https://github.com/Quick/Nimble/pull/953
    • @jessesquires made their first contribution in https://github.com/Quick/Nimble/pull/985

    Full Changelog: https://github.com/Quick/Nimble/compare/v9.2.0...v10.0.0

    Source code(tar.gz)
    Source code(zip)
  • v9.2.1(Aug 29, 2021)

  • v9.2.0(May 9, 2021)

    Additions

    • Add support for tuples of up to 6 elements to equal matcher, as with the standard library #880, #884

    Improvements

    • [CI] Split ci.yml into dedicated workflows #872
    • Suppress SwiftPM build warnings #878, #882

    Docs

    • [README] Use expect with trailing closure syntax for the succeed matcher #877, #883
    Source code(tar.gz)
    Source code(zip)
  • v9.1.0(May 7, 2021)

    Additions

    • Add satisfyAnyOf and satisfyAllOf overloads that take arrays of matchers #861, #869 (Thanks @younata)

    Improvements

    • Use target dependency condition instead of build machine info #842 (Thanks @kateinoigakukun)
    • [CI] Update Xcode and Swift versions #866, #871
    • Xcode 12.5: Update to recommended settings #868

    Bugfixes

    • Make the satisfyAllOf and satifyAnyOf matchers fail correctly when the submatchers fail #860 (Thanks @younata)
    Source code(tar.gz)
    Source code(zip)
  • v9.0.1(Apr 24, 2021)

    Improvements

    • NimbleXCTestHandler: replace deprecated recordFailure api with XCTIssue. #830 (Thanks @arangato)
    • Apply Xcode 12 recommended settings #835 (Thanks @arangato)
    • Bump cocoapods #840, #852
    • Bump norio-nomura/action-swiftlint #843, #846
    • Migrate Linux CI from Travis to GitHub Actions #857
    • Tweak SwiftLint configurations #858
    • [CI] Test Swift 5.4 #859

    Bugfixes

    • Replace deprecated libswiftXCTest.dylib with libXCTestSwiftSupport.dylib #863 (Thanks @evelynccordner)

    Docs

    • Update code sample to reflect comment #850 (Thanks @joemasilotti)
    Source code(tar.gz)
    Source code(zip)
  • v9.0.0(Oct 3, 2020)

    Nimble v9 has been released! :tada:

    The new major version requires Swift 5.2 / Xcode 11.4 at least, which also supports Swift 5.3 / Xcode 12 as well.

    Changes since v9.0.0-rc.3

    BREAKING CHANGES

    • Make beCloseTo matcher generic to fix a build error when used with allPass matcher on Swift 5.3 #832

    Improvements

    • Support ARM based Macs (Apple Silicon) #827 (Thanks @mRs-)
    • Upgrade Project Format to Xcode 11.4-compatible #828

    BREAKING CHANGES

    • Bump Swift requirement to 5.2 (Xcode 11.4) #689, #692, #701, #728
    • Make throwAssertion and throwError matchers generic and usable with non-void closures #698
    • Replaced TimeInterval with DispatchTimeInterval #718 (Thanks @wongzigii)
    • Deprecate Matcher-to-Predicate migration-path features as planned in the Deprecation Roadmap #745, #746
    • Add label to the second associated value of ExpectationMessage.expectedCustomValueTo for readability and understandability #751
    • Make raiseException generic and usable with non-void closures #752
    • Replace NMBMatcher return types with NMBPredicate #757
    • Move most of Objective-C matchers from NMBObjCMatcher to NMBPredicate #761
    • Convert Objective-C raiseException matcher to NMBPredicate #762
    • Convert Objective-C beCloseTo matcher to NMBPredicate #764
    • Convert Objective-C matchers' return types from id<NMBMatcher> to NMBPredicate #765
    • Deprecate NMBObjCMatcher which is not used inside Nimble anymore #767
    • Make postNotifications generic and usable with non-void closures #819
    • Bump minimum iOS version to 9.0 #825
    • [API BREAKING] Modify expect overloads to fix Swift 5.3 incompatibility #824
    • Make beCloseTo matcher generic to fix a build error when used with allPass matcher on Swift 5.3 #832

    Additions

    • Add elementsEqual(_:by:) matcher using a predicate closure #695
    • Implement assertion chaining #742, #750 (Thanks @mockersf)
    • Add beginWith(prefix:) matcher for sequence prefixes #719 (Thanks @Skoti)
    • Support passing NSExceptionName to raiseException matcher #776
    • Add postDistributedNotifications matcher for testing DistributedNotificationCenter #786 (Thanks @fabianmuecke)
    • Rename postNotifications(_:fromNotificationCenter:) to postNotifications(_:from:) #788

    Improvements

    • Update CwlPreconditionTesting to 2.0.0 #681, #706
    • [gardening] Move operators into types where possible #699
    • [CI] Parallel platform testing #700
    • Fix a swiftlint violation #726 (Thanks @chunkyguy)
    • Enable module stability #732
    • [CI] Use norio-nomura/action-swiftlint for running SwiftLint #735
    • [CI] Test the latest Swift Development Snapshot #738
    • Add default message to Predicate's convenience factory methods #743
    • Simplify RaisesExceptionTest #748
    • Enable parallel testing in Xcode #749
    • Add -Xlinker -no_application_extension to OTHER_LDFLAGS at project level #759
    • [gardening] Use guard where appropriate #763
    • Simplify generic parameters #768
    • Refactor message creation for errors and exceptions #769
    • Reduce Foundation import #770
    • Tweak CI #771
    • Remove now-unnecessary compiler version check #772
    • Unify some #if canImport(Darwin) conditional compilations #773
    • bundle update --bundler && bundle update #774
    • [Stringers] Remove Linux-related workaround in NSNumber.testDescription #777
    • Utilize as NSString bridging on Linux as well #778
    • Utilize as NSArray bridging on Linux as well #779
    • Utilize as NSDictionary bridging on Linux as well #780
    • Reduce #if canImport(Darwin) usages in tests #781
    • Remove SUPPORT_IMPLICIT_BRIDGING_CONVERSION conditional compilation #782
    • Use as bridging for NSNumber #783
    • Remove unused ExpectationMessage.sampleMessage #654
    • Refactor beEmpty matcher and add tests #785
    • Refactor postNotifications matchers #787
    • Create dependabot.yml #804
    • Bump cocoapods from 1.9.2 to 1.9.3 #805
    • Set ENABLE_TESTING_SEARCH_PATHS to YES #803
    • Test building for tvOS with Xcode-SwiftPM integration #818
    • Add DSL tests for expect variants #823
    • Support ARM based Macs (Apple Silicon) #827 (Thanks @mRs-)
    • Upgrade Project Format to Xcode 11.4-compatible #828

    Bugfixes

    • Fix elementsEqual matcher constraint to match with the Swift standard library equivalent #693
    • Stop using method swizzling for registering CurrentTestCaseTracker to XCTestObservationCenter #741
    • [tvOS] Remove CwlCatchException.h from public headers #760
    • [tvOS] Remove CwlMachBadInstructionHandler.h from public headers #766
    • Disambiguate postNotifications overloads #794
    • Switch back to use +[NSObject load] for registering CurrentTestCaseTracker #815

    Docs

    • Unpin the cocoapods instructions from Nimble 6.0.0 in the README #717 (Thanks @intiocean)
    • Fix README.md code example on Notifications #722 (Thanks @yanamura)
    • Fix indent in ISSUE_TEMPLATE and PULL_REQUEST_TEMPLATE #744
    • Reflect TimeInterval-to-DispatchTimeInterval changes to README #754
    • Update README to align with Matcher deprecation #758
    Source code(tar.gz)
    Source code(zip)
  • v9.0.0-rc.3(Sep 17, 2020)

    BREAKING CHANGES

    • Make postNotifications generic and usable with non-void closures #819
    • Bump minimum iOS version to 9.0 #825
    • [API BREAKING] Modify expect overloads to fix Swift 5.3 incompatibility #824

    Improvements

    • Set ENABLE_TESTING_SEARCH_PATHS to YES #803
    • Test building for tvOS with Xcode-SwiftPM integration #818
    • Add DSL tests for expect variants #823
    Source code(tar.gz)
    Source code(zip)
  • v9.0.0-rc.2(Sep 4, 2020)

    Additions

    • Add postDistributedNotifications matcher for testing DistributedNotificationCenter #786 (Thanks @fabianmuecke)
    • Rename postNotifications(_:fromNotificationCenter:) to postNotifications(_:from:) #788

    Improvements

    • Remove unused ExpectationMessage.sampleMessage #654
    • Refactor beEmpty matcher and add tests #785
    • Refactor postNotifications matchers #787
    • Create dependabot.yml #804
    • Bump cocoapods from 1.9.2 to 1.9.3 #805

    Bugfixes

    • Disambiguate postNotifications overloads #794
    • Switch back to use +[NSObject load] for registering CurrentTestCaseTracker #815
    Source code(tar.gz)
    Source code(zip)
  • v8.1.2(Sep 3, 2020)

  • v8.1.1(Jun 9, 2020)

  • v8.1.0(Jun 6, 2020)

    Additions

    • Rename postNotifications(_:fromNotificationCenter:) to postNotifications(_:from:) #789
      • postNotifications(_:fromNotificationCenter:) is deprecated and will be removed.

    Improvements

    • Fix CI builds #790
    • Tweak CI #791
    Source code(tar.gz)
    Source code(zip)
  • v9.0.0-rc.1(May 30, 2020)

    Additions

    • Support passing NSExceptionName to raiseException matcher #776

    Improvements

    • bundle update --bundler && bundle update #774
    • [Stringers] Remove Linux-related workaround in NSNumber.testDescription #777
    • Utilize as NSString bridging on Linux as well #778
    • Utilize as NSArray bridging on Linux as well #779
    • Utilize as NSDictionary bridging on Linux as well #780
    • Reduce #if canImport(Darwin) usages in tests #781
    • Remove SUPPORT_IMPLICIT_BRIDGING_CONVERSION conditional compilation #782
    • Use as bridging for NSNumber #783
    Source code(tar.gz)
    Source code(zip)
  • v9.0.0-beta.3(May 23, 2020)

    BREAKING CHANGES

    • Move most of Objective-C matchers from NMBObjCMatcher to NMBPredicate #761
    • Convert Objective-C raiseException matcher to NMBPredicate #762
    • Convert Objective-C beCloseTo matcher to NMBPredicate #764
    • Convert Objective-C matchers' return types from id<NMBMatcher> to NMBPredicate #765
    • Deprecate NMBObjCMatcher which is not used inside Nimble anymore #767

    Additions

    • Add beginWith(prefix:) matcher for sequence prefixes #719 (Thanks @Skoti)

    Improvements

    • Add -Xlinker -no_application_extension to OTHER_LDFLAGS at project level #759
    • [gardening] Use guard where appropriate #763
    • Simplify generic parameters #768
    • Refactor message creation for errors and exceptions #769
    • Reduce Foundation import #770
    • Tweak CI #771
    • Remove now-unnecessary compiler version check #772
    • Unify some #if canImport(Darwin) conditional compilations #773

    Bugfixes

    • [tvOS] Remove CwlCatchException.h from public headers #760
    • [tvOS] Remove CwlMachBadInstructionHandler.h from public headers #766
    Source code(tar.gz)
    Source code(zip)
  • v8.0.9(May 17, 2020)

  • v9.0.0-beta.2(May 16, 2020)

    BREAKING CHANGES

    • Deprecate Matcher-to-Predicate migration-path features as planned in the Deprecation Roadmap #745, #746
    • Add label to the second associated value of ExpectationMessage.expectedCustomValueTo for readability and understandability #751
    • Make raiseException generic and usable with non-void closures #752
    • Replace NMBMatcher return types with NMBPredicate #757

    Additions

    • Implement assertion chaining #742, #750 (Thanks @mockersf)

    Improvements

    • [CI] Test the latest Swift Development Snapshot #738
    • Add default message to Predicate's convenience factory methods #743
    • Simplify RaisesExceptionTest #748
    • Enable parallel testing in Xcode #749

    Bugfixes

    • Stop using method swizzling for registering CurrentTestCaseTracker to XCTestObservationCenter #741

    Docs

    • Fix indent in ISSUE_TEMPLATE and PULL_REQUEST_TEMPLATE #744
    • Reflect TimeInterval-to-DispatchTimeInterval changes to README #754
    • Update README to align with Matcher deprecation #758
    Source code(tar.gz)
    Source code(zip)
  • v8.0.8(May 14, 2020)

  • v9.0.0-beta.1(May 3, 2020)

    BREAKING CHANGES

    • Bump Swift requirement to 5.2 (Xcode 11.4) #689, #692, #701, #728
    • Make throwAssertion and throwError matchers generic and usable with non-void closures #698
    • Replaced TimeInterval with DispatchTimeInterval #718 (Thanks @wongzigii)

    Additions

    • Add elementsEqual(_:by:) matcher using a predicate closure #695

    Improvements

    • Update CwlPreconditionTesting to 2.0.0 #681, #706
    • [gardening] Move operators into types where possible #699
    • [CI] Parallel platform testing #700
    • Fix a swiftlint violation #726 (Thanks @chunkyguy)
    • Enable module stability #732
    • [CI] Use norio-nomura/action-swiftlint for running SwiftLint #735

    Bugfixes

    • Fix elementsEqual matcher constraint to match with the Swift standard library equivalent #693

    Docs

    • Unpin the cocoapods instructions from Nimble 6.0.0 in the README #717 (Thanks @intiocean)
    • Fix README.md code example on Notifications #722 (Thanks @yanamura)
    Source code(tar.gz)
    Source code(zip)
  • v8.0.7(Mar 29, 2020)

  • v8.0.6(Mar 28, 2020)

  • v8.0.5(Dec 28, 2019)

    Improvements

    • [CI] Fix GitHub Actions breakage and restore Travis build matrix for older Xcode versions #712, #715

    Bugfixes

    • Fix hanging toEventually when build with swift package manager (SPM) #711, #716 (Thanks @tomquist)
    Source code(tar.gz)
    Source code(zip)
  • v8.0.4(Sep 11, 2019)

  • v8.0.3(Sep 10, 2019)

    Improvements

    • Tweak build settings #670
    • Update CwlPreconditionTesting #675, #682
    • [CI] Use GitHub Actions for Darwin platforms #676, 679
    • [CI] Update Linux Swift versions #678
    • Support throwAssertion matcher on SwiftPM on Darwin #680
    • [CI] Test Xcode 11 #683
    • Reduce test duration in AsynchronousTest #684
    Source code(tar.gz)
    Source code(zip)
  • v8.0.2(Jun 8, 2019)

    Improvements

    • Update CocoaPods to 1.7.1 #640, #668
    • Support Swift 5's Package.swift syntax (platforms support) #641 (Thanks @devxoul)
    • [CocoaPods] Support swift_versions which is available since CocoaPods 1.7 #669
    • Update .hound.yml #645
    • [refactoring] Use canImport(Darwin) #647
    • [Xcode] Set DEPLOYMENT_TARGET and TARGETED_DEVICE_FAMILY build settings at target level #648
    • Xcode 10.2: Update to recommended settings #649
    • [refactoring] Remove compatibility hack below Swift 4.2 #651
    • Remove now-unnecessary Linux hack #652
    • Remove now-unnecessary platform checks #653
    • Suppress warnings #658
    • Tweak build settings #660
    • Reduce deprecated Matcher type usages in some matchers #663
    • Replace deprecated Predicate.matches(_:failureMessage:) and Predicate.doesNotMatch(_:failureMessage:) #664, #666
    • [gardening] Remove unnecessary parentheses #667

    Bugfixes

    • Fix ambiguities of beGreaterThan, beGreaterThanOrEqualTo, beLessThan and beLessThanOrEqualTo matchers on Linux with Swift 5.1 #659

    Docs

    • [README] Document support & installation via Accio #646 (Thanks @Dschee)
    Source code(tar.gz)
    Source code(zip)
  • v8.0.1(Mar 9, 2019)

  • v8.0.0(Mar 3, 2019)

    Nimble v8 has been released! :tada: The new major version requires Swift 4.2 / Xcode 10.1 at least, which also supports Swift 5.0 / Xcode 10.2 as well.

    BREAKING CHANGES

    • Migrate to Swift 4.1 and drop Swift 3.x support #515 (Thanks @ikesyo)
    • Bump MACOSX_DEPLOYMENT_TARGET to 10.10 #526 (Thanks @ikesyo)
    • Reduce NMBObjCMatcher usages #592, #600 (Thanks @ikesyo)
    • Bump Swift requirement to 4.2 #607, #628 (Thanks @heyzooi and @ikesyo)

    Additions

    • Sequence elementsEqual matcher #557 (Thanks @LucianoPAlmeida)
    • Add beEmpty matcher for SetAlgebra #594 (Thanks @ikesyo)
    • Add contain matcher for SetAlgebra #609 (Thanks @ikesyo)

    Improvements

    • Swift 4.1: Adopt LinuxMain generation feature by swift test --generate-linuxmain #516 (Thanks @ikesyo)
    • Remove equal matcher overloads for [T: C]? and [T]? thanks to Conditional Conformance #521 (Thanks @ikesyo)
    • [gardening][BeVoid] Move operators into types #522 (Thanks @ikesyo)
    • Simplify equal matcher for [T?] #524 (Thanks @ikesyo)
    • [Xcode] Enable New Build System #527 (Thanks @ikesyo)
    • Utilize multiline string literals #544 (Thanks @ikesyo)
    • [SwiftLint] Remove force_cast from disabled_rules #562 (Thanks @ikesyo)
    • [SwiftLint] Remove identifier_name from disabled_rules #563 (Thanks @ikesyo)
    • [SwiftLint] Remove force_try from disabled_rules #582 (Thanks @ikesyo)
    • Rename Sequence.all to allSatisfy to match SE-0207 #583 (Thanks @ikesyo)
    • Remove workaround to SR-2290 #593 (Thanks @norio-nomura)
    • [SwiftLint] Remove todo from disabled_rules #602
    • Test Swift 5.0 (5.0-DEVELOPMENT-SNAPSHOT-2019-02-05-a) #629 (Thanks @ikesyo)
    • [CI] Test with Xcode 10.2 #637 (Thanks @ikesyo)

    Bugfixes

    • 🐛Workaround to noescape bug in Xcode 10 beta 1 #537 (Thanks @phatblat)
    • Revert "🐛Workaround to noescape bug in Xcode 10 beta 1" #560 (Thanks @ikesyo)
    • [podspec] Remove duplicate swift_version setting #630 (Thanks @ikesyo)

    Docs

    • Add a "Reviewed by Hound" badge #586 (Thanks @salbertson)
    Source code(tar.gz)
    Source code(zip)
Owner
Quick
The Swift (and Objective-C) testing framework.
Quick
A light-weight TDD / BDD framework for Objective-C & Cocoa

Specta A light-weight TDD / BDD framework for Objective-C. FEATURES An Objective-C RSpec-like BDD DSL Quick and easy set up Built on top of XCTest Exc

Specta / Expecta 2.3k Dec 20, 2022
Code coverage for Xcode projects (Objective-C only)

XcodeCoverage provides a simple way to generate reports of the Objective-C code coverage of your Xcode project. Generated reports include HTML and Cob

Jon Reid 854 Dec 19, 2022
BDD Framework and test runner for Swift projects and playgrounds

Spectre Special Executive for Command-line Test Running and Execution. A behavior-driven development (BDD) framework and test runner for Swift project

Kyle Fuller 392 Jan 1, 2023
A simple and lightweight matching library for XCTest framework.

Match A simple and lightweight matching library for XCTest framework. Getting started Swift Package Manager You can add Match to your project by addin

Michał Tynior 6 Oct 23, 2022
Switchboard - easy and super light weight A/B testing for your mobile iPhone or android app. This mobile A/B testing framework allows you with minimal servers to run large amounts of mobile users.

Switchboard - easy A/B testing for your mobile app What it does Switchboard is a simple way to remote control your mobile application even after you'v

Keepsafe 287 Nov 19, 2022
Remote configuration and A/B Testing framework for iOS

MSActiveConfig v1.0.1 Remote configuration and A/B Testing framework for iOS. Documentation available online. MSActiveConfig at a glance One of the bi

Elevate 78 Jan 13, 2021
T - A simple testing framework using closures and errors

t Quickly test expectations What is t? t is a simple testing framework using clo

OpenBytes 6 Nov 7, 2022
AutoMocker is a Swift framework that leverages the type system to let you easily create mocked instances of your data types.

AutoMocker Context AutoMocker is a Swift framework that leverages the type system to let you easily create mocked instances of your data types. Here's

Vincent Pradeilles 39 May 19, 2022
Boilerplate-free mocking framework for Swift!

Cuckoo Mock your Swift objects! Introduction Cuckoo was created due to lack of a proper Swift mocking framework. We built the DSL to be very similar t

Brightify 1.5k Dec 27, 2022
Mockit is a Tasty mocking framework for unit tests in Swift 5.0

Mockit Introduction Mockit is a Tasty mocking framework for unit tests in Swift 5.0. It's at an early stage of development, but its current features a

Syed Sabir Salman-Al-Musawi 118 Oct 17, 2022
BDD-style framework for Swift

Sleipnir Sleipnir is a BDD-style framework for Swift. Sleipnir is highly inspired by Cedar. Also In Norse mythology, Sleipnir is Odin's steed, is the

Railsware 846 Nov 22, 2022
A convenient mocking framework for Swift

// Mocking let bird = mock(Bird.self) // Stubbing given(bird.getName()).willReturn("Ryan") // Verification verify(bird.fly()).wasCalled() What is Mo

Bird Rides, Inc 545 Jan 5, 2023
A mocking framework for Swift

SwiftMock SwiftMock is a mocking framework for Swift 5.2. Notes on the history of this repo September 2015: first version of this framework November 2

Matthew Flint 257 Dec 14, 2022
Swift Framework for TestRail's API

QuizTrain ?? ?? QuizTrain is a framework created at Venmo allowing you to interact with TestRail's API using Swift. It supports iOS, macOS, tvOS, and

Venmo 18 Mar 17, 2022
Swift framework containing a set of helpful XCTest extensions for writing UI automation tests

AutoMate • AppBuddy • Templates • ModelGenie AutoMate AutoMate is a Swift framework containing a set of helpful XCTest extensions for writing UI autom

PGS Software 274 Dec 30, 2022
Genything is a framework for random testing of a program properties.

Genything is a framework for random testing of a program properties. It provides way to random data based on simple and complex types.

Just Eat Takeaway.com 25 Jun 13, 2022
AB testing framework for iOS

ABKit Split Testing for Swift. ABKit is a library for implementing a simple Split Test that: Doesn't require an HTTP client written in Pure Swift Inst

Recruit Marketing Partners Co.,Ltd 113 Nov 11, 2022
Keep It Functional - An iOS Functional Testing Framework

IMPORTANT! Even though KIF is used to test your UI, you need to add it to your Unit Test target, not your UI Test target. The magic of KIF is that it

KIF Framework 6.2k Dec 29, 2022
iOS UI Automation Test Framework

Deprecation: EarlGrey 1.0 is deprecated in favor of EarlGrey 2.0 which integrates it with XCUITest. Please look at the earlgrey2 branch. EarlGrey 1.0

Google 5.5k Dec 30, 2022