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 allows you to drive your UI from your unit tests and reap all the advantages of testing in-process.
KIF, which stands for Keep It Functional, is an iOS integration test framework. It allows for easy automation of iOS apps by leveraging the accessibility attributes that the OS makes available for those with visual disabilities.
KIF builds and performs the tests using a standard XCTest
testing target. Testing is conducted synchronously in the main thread (running the run loop to force the passage of time) allowing for more complex logic and composition. This also allows KIF to take advantage of the Xcode Test Navigator, command line build tools, and Bot test reports.
KIF uses undocumented Apple APIs. This is true of most iOS testing frameworks, and is safe for testing purposes, but it's important that KIF does not make it into production code, as it will get your app submission denied by Apple. Follow the instructions below to ensure that KIF is configured correctly for your project.
All of the tests for KIF are written in Objective-C. This allows for maximum integration with your code while minimizing the number of layers you have to build.
KIF integrates directly into your Xcode project, so there's no need to run an additional web server or install any additional packages.
KIF's test suite has been run against iOS 8.1 and above, though lower versions will likely work.
KIF attempts to imitate actual user input. Automation is done using tap events wherever possible.
You can easily run a single KIF test with the Test Navigator or kick off nightly acceptance tests with Bots.
KIF uses techniques described below to validate its internal functionality. You can see a test suite that exercises its entire functionality by simply building and testing the KIF scheme with ⌘U. Look at the tests in the "KIF Tests" group for ideas on how to build your own tests.
CocoaPods are the easiest way to get set up with KIF.
The first thing you will want to do is set up a test target you will be using for KIF. You may already have one named MyApplication_Tests if you selected to automatically create unit tests. If you did, you can keep using it if you aren't using it for unit tests. Otherwise, follow these directions to create a new one.
Select your project in Xcode and click on "Add Target" in the bottom left corner of the editor. Select iOS -> Test -> iOS Unit Testing Bundle. Give it a product name like "Acceptance Tests", "UI Tests", or something that indicates the intent of your testing process.
The testing target will add a header and implementation file, likely "Acceptance_Tests.m/h" to match your target name. Delete those.
Once your test target set up, add the following to your Podfile file. Use your target's name as appropriate.
target 'Your Apps' do
...
end
target 'Acceptance Tests' do
pod 'KIF', :configurations => ['Debug']
end
After running pod install
complete the tasks in Final Test Target Configurations below for the final details on getting your tests to run.
To install KIF, you'll need to link the libKIF static library directly into your application. Download the source from the kif-framework/KIF and follow the instructions below. The screenshots are from Xcode 6 on Yosemite, but the instructions should be the same for Xcode 5 or later on any OS version.
We'll be using a simple project as an example, and you can find it in Documentation/Examples/Testable Swift
in this repository.
The first step is to add the KIF project into the ./Frameworks/KIF subdirectory of your existing app. If your project uses Git for version control, you can use submodules to make updating in the future easier:
cd /path/to/MyApplicationSource
mkdir Frameworks
git submodule add https://github.com/kif-framework/KIF.git Frameworks/KIF
If you're not using Git, simply download the source and copy it into the ./Frameworks/KIF
directory.
Let your project know about KIF by adding the KIF project into a workspace along with your main project. Find the KIF.xcodeproj
file in Finder and drag it into the Project Navigator (⌘1).
You'll need to create a test target for your app. You may already have one named MyApplicationTests if you selected to automatically create unit tests when you created the project. If you did, you can keep using it if you aren't using it for unit tests. Otherwise, follow these directions to create a new one.
Select your project in Xcode and click on "Add Target" in the bottom left corner of the editor. Select iOS -> Test -> iOS Unit Testing Bundle. Give it a product name like "Acceptance Tests", "UI Tests", or something that indicates the intent of your testing process.
The testing target will add a header and implementation file, likely "Acceptance_Tests.m/h" to match your target name. Delete those.
Now that you have a target for your tests, add the tests to that target. With the project settings still selected in the Project Navigator, and the new integration tests target selected in the project settings, select the "Build Phases" tab. Under the "Link Binary With Libraries" section, hit the "+" button. In the sheet that appears, select "libKIF.a" and click "Add". Repeat the process for CoreGraphics.framework and QuartzCore.framework.
KIF requires the IOKit.framework, but it is not located with the other system frameworks. To link to it, add "-framework IOKit" to the "Other Linker Flags" build setting.
KIF takes advantage of Objective C's ability to add categories on an object, but this isn't enabled for static libraries by default. To enable this, add the -ObjC
flag to the "Other Linker Flags" build setting on your test bundle target as shown below.
Read Final Test Target Configurations below for the final details on getting your tests to run.
Normally you identify a UI element via its accessibility label so that KIF simulates the interactions of a real user as closely as possible. In some cases, however, you may have to use accessibility identifiers, which are not exposed to users. If using CocoaPods, install the additional identifier-based KIF tests via the Identifier CocoaPods subspec:
pod 'KIF/IdentifierTests'
If not using CocoaPods, the identifier-based KIF tests can be added by including "KIFUITestActor-IdentifierTests.h".
You need your tests to run hosted in your application. Xcode does this for you by default when creating a new testing bundle target, but if you're migrating an older bundle, follow the steps below.
First add your application by selecting "Build Phases", expanding the "Target Dependencies" section, clicking on the "+" button, and in the new sheet that appears selecting your application target and clicking "Add".
Next, configure your bundle loader. In "Build Settings", expand "Linking" and edit "Bundle Loader" to be "$(TEST_HOST)". Expand the "Testing" section and edit "Test Host" to be "$(BUILT_PRODUCTS_DIR)/MyApplication.app/MyApplication" where "MyApplication" is the name of your app. Also make sure that "Wrapper Extension" is set to "xctest".
The last step is to configure your unit tests to run when you trigger a test (⌘U). Click on your scheme name and select "Edit Scheme…". Click on "Test" in the sidebar followed by the "+" in the bottom left corner. Select your testing target and click "OK".
With your project configured to use KIF, it's time to start writing tests. There are two main classes used in KIF testing: the test case (KIFTestCase
, subclass of XCTestCase
) and the UI test actor (KIFUITestActor
). The XCTest test runner loads the test case classes and executes their test. Inside these tests, the tester performs the UI operations which generally imitate a user interaction. Three of the most common tester actions are "tap this view," "enter text into this view," and "wait for this view." These steps are included as factory methods on KIFUITestActor
in the base KIF implementation.
KIF relies on the built-in accessibility of iOS to perform its test steps. As such, it's important that your app is fully accessible. This is also a great way to ensure that your app is usable by everyone. Giving your views reasonable labels is usually a good place to start when making your application accessible. More details are available in Apple's Documentation.
The first step is to create a test class to test some functionality. In our case, we will create a login test (LoginTests
). Create a new class that inherits from KIFTestCase. You may have to update the import to point to <KIF/KIF.h>
. The test method name provides a unique identifier. Your KIFTestCase
subclass should look something like this:
LoginTestCase.h
#import <KIF/KIF.h>
@interface LoginTests : KIFTestCase
@end
LoginTestCase.m
#import "LoginTests.h"
#import "KIFUITestActor+EXAdditions.h"
@implementation LoginTests
- (void)beforeEach
{
[tester navigateToLoginPage];
}
- (void)afterEach
{
[tester returnToLoggedOutHomeScreen];
}
- (void)testSuccessfulLogin
{
[tester enterText:@"[email protected]" intoViewWithAccessibilityLabel:@"Login User Name"];
[tester enterText:@"thisismypassword" intoViewWithAccessibilityLabel:@"Login Password"];
[tester tapViewWithAccessibilityLabel:@"Log In"];
// Verify that the login succeeded
[tester waitForTappableViewWithAccessibilityLabel:@"Welcome"];
}
@end
Most of the tester actions in the test are already defined by the KIF framework, but -navigateToLoginPage
and -returnToLoggedOutHomeScreen
are not. These are examples of custom actions which are specific to your application. Adding such steps is easy, and is done using a factory method in a category of KIFUITestActor
, similar to how we added the scenario.
KIFUITestActor+EXAdditions.h
#import <KIF/KIF.h>
@interface KIFUITestActor (EXAdditions)
- (void)navigateToLoginPage;
- (void)returnToLoggedOutHomeScreen;
@end
KIFUITestActor+EXAdditions.m
#import "KIFUITestActor+EXAdditions.h"
@implementation KIFUITestActor (EXAdditions)
- (void)navigateToLoginPage
{
[self tapViewWithAccessibilityLabel:@"Login/Sign Up"];
[self tapViewWithAccessibilityLabel:@"Skip this ad"];
}
- (void)returnToLoggedOutHomeScreen
{
[self tapViewWithAccessibilityLabel:@"Logout"];
[self tapViewWithAccessibilityLabel:@"Logout"]; // Dismiss alert.
}
@end
Everything should now be configured. When you run the integration tests using the test button, ⌘U, or the Xcode 5 Test Navigator (⌘5).
KIFTestCase
is not necessary for running KIF tests. Tests can run directly in XCTestCase
or any subclass. The basic requirement is that when you call tester
or system
, self
must be an instance of XCTestCase
.
For example, the following Specta test works without any changes to KIF or Specta:
#import <Specta.h>
#import <KIF.h>
SpecBegin(App)
describe(@"Tab controller", ^{
it(@"should show second view when I tap on the second tab", ^{
[tester tapViewWithAccessibilityLabel:@"Second" traits:UIAccessibilityTraitButton];
[tester waitForViewWithAccessibilityLabel:@"Second View"];
});
});
SpecEnd
If you want to use KIF with a test runner that does not subclass XCTestCase
, your runner class just needs to implement the KIFTestActorDelegate
protocol which contains two required methods.
- (void)failWithException:(NSException *)exception stopTest:(BOOL)stop;
- (void)failWithExceptions:(NSArray *)exceptions stopTest:(BOOL)stop;
In the first case, the test runner should log the exception and halt the test execution if stop
is YES
. In the second, the runner should log all the exceptions and halt the test execution if stop
is YES
. The exceptions take advantage of KIF's extensions to NSException
that include the lineNumber
and filename
in the exception's userData
to record the error's origin.
Since it's easy to combine Swift and Objective-C code in a single project, KIF is fully capable of testing apps written in both Objective-C and Swift.
If you want to write your test cases in Swift, you'll need to keep two things in mind.
- Your test bundle's bridging header will need to
#import <KIF/KIF.h>
, since KIF is a static library and not a header. - The
tester
andsystem
keywords are C preprocessor macros which aren't available in Swift. You can easily write a small extension toXCTestCase
or any other class to access them:
extension XCTestCase {
func tester(file : String = #file, _ line : Int = #line) -> KIFUITestActor {
return KIFUITestActor(inFile: file, atLine: line, delegate: self)
}
func system(file : String = #file, _ line : Int = #line) -> KIFSystemTestActor {
return KIFSystemTestActor(inFile: file, atLine: line, delegate: self)
}
}
extension KIFTestActor {
func tester(file : String = #file, _ line : Int = #line) -> KIFUITestActor {
return KIFUITestActor(inFile: file, atLine: line, delegate: self)
}
func system(file : String = #file, _ line : Int = #line) -> KIFSystemTestActor {
return KIFSystemTestActor(inFile: file, atLine: line, delegate: self)
}
}
See Documentation/Examples/Testable Swift for sample code.
This issue occurs when XCTest does not have a valid test host. Reread the instructions above with regards to the "Bundle Loader" and "Test Host" settings. You may have missed something.
If KIF is failing to find a view, the most likely cause is that the view doesn't have its accessibility label set. If the view is defined in a xib, then the label can be set using the inspector. If it's created programmatically, simply set the accessibilityLabel attribute to the desired label.
If the label is definitely set correctly, take a closer look at the error given by KIF. This error should tell you more specifically why the view was not accessible. If you are using -waitForTappableViewWithAccessibilityLabel:
, then make sure the view is actually tappable. For items such as labels which cannot become the first responder, you may need to use -waitForViewWithAccessibilityLabel:
instead.
If the first time you try to run KIF you get the following error:
2011-06-13 13:54:53.295 Testable (Integration Tests)[12385:207] -[NSFileManager createUserDirectory:]: unrecognized selector sent to instance 0x4e02830
2011-06-13 13:54:53.298 Testable (Integration Tests)[12385:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSFileManager createUserDirectory:]: unrecognized selector sent to instance 0x4e02830'
or if you get another "unrecognized selector" error inside the KIF code, make sure that you've properly set the -ObjC flag as described above. Without this flag your app can't access the category methods that are necessary for KIF to work properly.
A continuous integration (CI) process is highly recommended and is extremely useful in ensuring that your application stays functional. The easiest way to do this will be with Xcode 5, either using Bots, or Jenkins or another tool that uses xcodebuild. For tools using xcodebuild, review the manpage for instructions on using test destinations.
We're glad you're interested in KIF, and we'd love to see where you take it.
Any contributors to the master KIF repository must sign the Individual Contributor License Agreement (CLA). It's a short form that covers our bases and makes sure you're eligible to contribute.
When you have a change you'd like to see in the master repository, send a pull request. Before we merge your request, we'll make sure you're in the list of people who have signed a CLA.
Thanks, and happy testing!