We’ll start by writing a very simple Xcode unit test, but instead of using XCTest’s
XCTAssertEqualObjects function, we’ll use OCHamcrest’s assertThat construct and a predefined
matcher:
The assertThat function is a stylized sentence for making a test assertion. In this example, the
subject of the assertion is the object theBiscuit, which is the first method parameter. The second
method parameter is a matcher for Biscuit objects, here a matcher that checks one object is equal
to another using the -isEqual: method. The test passes since the Biscuit class defines an
-isEqual: method.
OCHamcrest’s functions are actually declared with an “HC_” package prefix (such as HC_assertThat
and HC_equalTo) to avoid name clashes. To make test writing faster and test code more legible,
optional short syntax is provided by default. For example, instead of writing HC_assertThat,
simply write assertThat.
Predefined Matchers
OCHamcrest comes with a library of useful matchers:
Object
conformsTo - match object that conforms to protocol
equalTo - match equal object
hasDescription - match object’s -description
hasProperty - match return value of method with given name
instanceOf - match object type
isA - match object type precisely, no subclasses
nilValue, notNilValue - match nil, or not nil
sameInstance - match same object
throwsException - match block that throws an exception
HCArgumentCaptor - match anything, capturing all values
Number
closeTo - match number close to a given value
greaterThan, greaterThanOrEqualTo, lessThan,
lessThanOrEqualTo - match numeric ordering
isFalse - match zero
isTrue - match non-zero
Text
containsSubstring - match part of a string
endsWith - match the end of a string
equalToIgnoringCase - match the complete string but ignore case
equalToIgnoringWhitespace - match the complete string but ignore extra
whitespace
startsWith - match the beginning of a string
stringContainsInOrder, stringContainsInOrderIn - match parts of a string, in relative order
Logical
allOf, allOfIn - “and” together all matchers
anyOf, anyOfIn - “or” together all matchers
anything - match anything (useful in composite matchers when you don’t
care about a particular value)
isNot - negate the matcher
Collection
contains, containsIn - exactly match the entire collection
containsInAnyOrder, containsInAnyOrderIn - match the entire collection, but in any order
containsInRelativeOrder, containsInRelativeOrderIn - match collection containing items in relative order
everyItem - match if every item in a collection satisfies a given matcher
hasCount - match number of elements against another matcher
hasCountOf - match collection with given number of elements
hasEntries - match dictionary with key-value pairs in a dictionary
hasEntriesIn - match dictionary with key-value pairs in a list
hasEntry - match dictionary containing a key-value pair
hasItem - match if given item appears in the collection
hasItems, hasItemsIn - match if all given items appear in the collection, in any order
hasKey - match dictionary with a key
hasValue - match dictionary with a value
isEmpty - match empty collection
isIn - match when object is in given collection
onlyContains, onlyContainsIn - match if collection’s items appear in given list
Decorator
describedAs - give the matcher a custom failure description
is - decorator to improve readability - see “Syntactic sugar” below
The arguments for many of these matchers accept not just a matching value, but
another matcher, so matchers can be composed for greater flexibility. For
example, only_contains(endsWith(@".")) will match any collection where every
item is a string ending with period.
Syntactic Sugar
OCHamcrest strives to make your tests as readable as possible. For example, the is matcher is a
wrapper that doesn’t add any extra behavior to the underlying matcher. The following assertions are
all equivalent:
The last form is allowed since is wraps non-matcher arguments with equalTo. Other matchers that
take matchers as arguments provide similar shortcuts, wrapping non-matcher arguments in equalTo.
Common Questions
How Can I Assert on an Asynchronous Call?
assertWithTimeout will keep evaluating an expression until the matcher is satisfied or a timeout
is reached. For example,
This repeatedly checks for this string to evaluate to “expected” before timing out after 5 seconds.
thatEventually is a convenience macro to create a block.
Can I Add Custom Matchers?
OCHamcrest comes bundled with lots of useful matchers, but you’ll probably find that you need to
create your own from time to time to fit your testing needs. See the
“Writing Custom Matchers” guide for more information.
If you want to add OCHamcrest using Cocoapods then add the following dependency to your Podfile.
Most people will want OCHamcrest in their test targets, and not include any pods from their main
targets:
target 'MyTests' do
inherit! :search_paths
use_frameworks!
pod 'OCHamcrest', '~> 9.0'
end
Carthage
Add the following to your Cartfile:
github "hamcrest/OCHamcrest" ~> 9.0
Then drag the the built framework from the appropriate Carthage/Build directory into your project,
but with “Copy items into destination group’s folder” disabled.
Prebuilt Framework
A prebuilt binary is available on GitHub. The
binary is packaged as OCHamcrest.xcframework, containing these architectures:
macOS
Mac Catalyst
iOS device
iOS simulator
tvOS device
tvOS simulator
watchOS device
watchOS simulator
Drag the XCFramework into your project.
Build Your Own
If you want to build OCHamcrest yourself, clone the repo, then
OCHamcrest
OCHamcrest is an Objective-C module providing:
Matchers can be combined to create flexible expressions of intent in tests. They can also be used for other purposes, such as user input validation.
Contents
My First OCHamcrest Test
We’ll start by writing a very simple Xcode unit test, but instead of using XCTest’s
XCTAssertEqualObjects
function, we’ll use OCHamcrest’sassertThat
construct and a predefined matcher:The
assertThat
function is a stylized sentence for making a test assertion. In this example, the subject of the assertion is the objecttheBiscuit
, which is the first method parameter. The second method parameter is a matcher forBiscuit
objects, here a matcher that checks one object is equal to another using the-isEqual:
method. The test passes since theBiscuit
class defines an-isEqual:
method.OCHamcrest’s functions are actually declared with an “HC_” package prefix (such as
HC_assertThat
andHC_equalTo
) to avoid name clashes. To make test writing faster and test code more legible, optional short syntax is provided by default. For example, instead of writingHC_assertThat
, simply writeassertThat
.Predefined Matchers
OCHamcrest comes with a library of useful matchers:
Object
conformsTo
- match object that conforms to protocolequalTo
- match equal objecthasDescription
- match object’s-description
hasProperty
- match return value of method with given nameinstanceOf
- match object typeisA
- match object type precisely, no subclassesnilValue
,notNilValue
- matchnil
, or notnil
sameInstance
- match same objectthrowsException
- match block that throws an exceptionNumber
closeTo
- match number close to a given valuegreaterThan
,greaterThanOrEqualTo
,lessThan
,lessThanOrEqualTo
- match numeric orderingisFalse
- match zeroisTrue
- match non-zeroText
containsSubstring
- match part of a stringendsWith
- match the end of a stringequalToIgnoringCase
- match the complete string but ignore caseequalToIgnoringWhitespace
- match the complete string but ignore extra whitespacestartsWith
- match the beginning of a stringstringContainsInOrder
,stringContainsInOrderIn
- match parts of a string, in relative orderLogical
allOf
,allOfIn
- “and” together all matchersanyOf
,anyOfIn
- “or” together all matchersanything
- match anything (useful in composite matchers when you don’t care about a particular value)isNot
- negate the matcherCollection
contains
,containsIn
- exactly match the entire collectioncontainsInAnyOrder
,containsInAnyOrderIn
- match the entire collection, but in any ordercontainsInRelativeOrder
,containsInRelativeOrderIn
- match collection containing items in relative ordereveryItem
- match if every item in a collection satisfies a given matcherhasCount
- match number of elements against another matcherhasCountOf
- match collection with given number of elementshasEntries
- match dictionary with key-value pairs in a dictionaryhasEntriesIn
- match dictionary with key-value pairs in a listhasEntry
- match dictionary containing a key-value pairhasItem
- match if given item appears in the collectionhasItems
,hasItemsIn
- match if all given items appear in the collection, in any orderhasKey
- match dictionary with a keyhasValue
- match dictionary with a valueisEmpty
- match empty collectionisIn
- match when object is in given collectiononlyContains
,onlyContainsIn
- match if collection’s items appear in given listDecorator
describedAs
- give the matcher a custom failure descriptionis
- decorator to improve readability - see “Syntactic sugar” belowThe arguments for many of these matchers accept not just a matching value, but another matcher, so matchers can be composed for greater flexibility. For example,
only_contains(endsWith(@"."))
will match any collection where every item is a string ending with period.Syntactic Sugar
OCHamcrest strives to make your tests as readable as possible. For example, the
is
matcher is a wrapper that doesn’t add any extra behavior to the underlying matcher. The following assertions are all equivalent:The last form is allowed since
is
wraps non-matcher arguments withequalTo
. Other matchers that take matchers as arguments provide similar shortcuts, wrapping non-matcher arguments inequalTo
.Common Questions
How Can I Assert on an Asynchronous Call?
assertWithTimeout
will keep evaluating an expression until the matcher is satisfied or a timeout is reached. For example,This repeatedly checks for this string to evaluate to “expected” before timing out after 5 seconds.
thatEventually
is a convenience macro to create a block.Can I Add Custom Matchers?
OCHamcrest comes bundled with lots of useful matchers, but you’ll probably find that you need to create your own from time to time to fit your testing needs. See the “Writing Custom Matchers” guide for more information.
What About Swift?
Try the native Swift implementation of Hamcrest.
How Do I Add OCHamcrest to My Project?
The Examples folder shows projects ready to use OCHamcrest via Swift Package Manager, CocoaPods, or through the prebuilt framework.
Swift Package Manager
Include an OCHamcrest package in your Package.swift manifest’s array of
dependencies
:snippet source | anchor
Then add OCHamcrest to the dependencies of your
.testTarget
:snippet source | anchor
CocoaPods
If you want to add OCHamcrest using Cocoapods then add the following dependency to your Podfile. Most people will want OCHamcrest in their test targets, and not include any pods from their main targets:
Carthage
Add the following to your Cartfile:
Then drag the the built framework from the appropriate Carthage/Build directory into your project, but with “Copy items into destination group’s folder” disabled.
Prebuilt Framework
A prebuilt binary is available on GitHub. The binary is packaged as OCHamcrest.xcframework, containing these architectures:
Drag the XCFramework into your project.
Build Your Own
If you want to build OCHamcrest yourself, clone the repo, then