Integration Testing is a different concept to Unit Testing. It is the idea of testing changes in aggregate, as opposed to individual units. A good testing goal is to have a lot of the finer-grained ( thinner brush ) tests covered by unit testing, then Integration Testing will help you deal with larger ideas ( a paint roller. )
Within the context of Cocoa, integration tests generally means writing tests against things you have no control over. Which you could argue is all of UIKit, but hey, gotta do that to build an app. Seriously though, UIKit is the most common thing against which people have done integration testing.
UI Testing involves running your app as though there was a human on the other side tapping buttons, waiting for animations and filling in all of bits of data. The APIs make it easy to make tests like "If I've not added an email, is the submit button disabled?" and "After hitting submit with credentials, do it go to the home screen?" These let you write tests pretty quickly ( it's now built into Xcode ) and it can be used to provide a lot of coverage fast.
The tooling for in the OSS world is pretty mature now. The dominant player is Square's KIF. KIF's tests generally look like this:
class ReaderViewUITests: KIFTestCase, UITextFieldDelegate {
[...]
func markAsReadFromReaderView() {
tester().tapViewWithAccessibilityLabel("Mark as Read")
tester().tapViewWithAccessibilityIdentifier("url")
tester().tapViewWithAccessibilityLabel("Reading list")
tester().swipeViewWithAccessibilityLabel("Reader View Test", inDirection: KIFSwipeDirection.Right)
tester().waitForViewWithAccessibilityLabel("Mark as Unread")
tester().tapViewWithAccessibilityLabel("Cancel")
}
[...]
}
Where KIF will look or wait for specific views in the view hierarchy, then perform some actions. Apple's version of KIF, UITesting is similar, but different.
It works by having a completely different test target just for UI Integration Tests, separate from your Unit Tests. It can build out your test-suite much faster, as it can record the things you click on in the simulator, and save the actions to your source files in Xcode.
These tests look like vanilla XCTest, here's some examples from Deck-Tracker
class About: XCTestCase {
let backButton = XCUIApplication().navigationBars["About"].buttons["Settings"]
let aboutTitleScreen = XCUIApplication().navigationBars["About"].staticTexts["About"]
let hearthstoneImage = XCUIApplication().images["Hearthstone About"]
[...]
override func setUp() {
super.setUp()
continueAfterFailure = false
XCUIApplication().launch()
let app = XCUIApplication()
app.navigationBars["Games List"].buttons["More Info"].tap()
app.tables.staticTexts["About"].tap()
}
func testElementsOnScreen() {
XCTAssert(backButton.exists)
XCTAssert(aboutTitleScreen.exists)
XCTAssert(hearthstoneImage.exists)
XCTAssert(versionNumberLabel.exists)
XCTAssert(createdByLabel.exists)
XCTAssert(emailButton.exists)
XCTAssert(nounIconsLabel.exists)
}
[...]
}
There are some good up-sides to this approach, it's really fast to set up and to re-create when something changes. It's a really wide-brushed approach to covering your tested.
The biggest down-side is that it's slow. It requires running all the usual animations and networking would be performed as usual in the app. These can be worked around with some networking stubbing libraries, mainly VCR or HTTP Stubs but that adds a lot more complexity to what is generally a simple approach.
If you have a staging environment for your API, it can be worth having your application run through a series of real-world networking tasks to verify the APIs which you rely on ( but don't necessarily maintain) continue to act in the way you expect.
This can normally be built with KIF/UITesting, and can be tested