Now that you’ve written your first KIF test or two, there’s a couple more KIF tips and tricks I wanted to share with you. Nothing too fancy, just a couple nice touches I’ve developed while writing KIF tests.
Don’t Forget About XCTestCase
KIFTestCase
is a subclass of XCTestCase
. This means that all the goodness of the XCTest framework is available to you in KIF tests. This makes for some really nice KIF tips and tricks.
XCTAssertEqual, XCTAssertTrue and related methods
These are all methods that would be familiar to anyone writing unit tests. These are the meat of how you make assertions about outcomes and expectations when writing unit tests. You can do the same thing in KIF tests. It’s especially powerful when combined with tester().waitForViewWithAccessibilityLabel(String)
since that method returns a UIView
. You can cast that view to a UIView
subclass, and then access any custom properties on it, and then make assertions.
For example, suppose you have a view that should change colors in response to a button being pressed. You could write this KIF test:
func testViewChangesColor_WhenButtonPressed() {
tester().tapViewWithAccessibilityLabel("Some View")
let redView = tester().waitForViewWithAccessibilityLabel("the supposed red view")
XCTAssertEqual(redView.backgroundColor, UIColor.redColor())
}
In this test, you programmatically tap a view with a given accessibility label, presumably the button. Then, you get a reference to the view that should have changed colors, and make an assertion on its background color.
setUp(), tearDown(), beforeAll(), and afterAll()
setUp()
, beforeAll()
, and tearDown()
are powerful methods that help you do common legwork before or after tests run. They help to stabilize state between tests, and remove redundant code by providing a single place for it to be executed. setUp()
and tearDown()
run before and after each test method in the test class. These are really useful if each test needs to assume some sort of initial state. Imagine you are testing a view that represents a form. Before each test, you want that form to be in a clean state. These methods can enable you to clean up, or set some intial state before each test runs.
beforeAll()
and afterAll()
run before or after all tests in a given test class. These are useful when a given test class contains tests for a certain view in the app, that isn’t the initial view of the app. Say you are trying to test the third view controller deep in a navigation stack. It would be appropriate in beforeAll()
to navigate down the stack to the view to test, and then in afterAll()
to pop back up to the root view for other tests to run.
This leads me to the next item in my KIF tips and tricks, some suggestions on how to break up your test classes.
Segmenting Your Tests
The key to maintainable KIF tests is good segmentation of what you’re testing, across different tests and test suites. A “test” refers to a single function in a KIFTestCase
subclass. A “test suite” refers to an entire KIFTestCase
subclass, and all the tests within it. I don’t have any hard and fast rules on how I break up my KIF tests. Thinking through my KIF tips and tricks, I would phrase my suggested best practice as, group tests of related functionality into a single test suite, while keeping your tests themselves standalone and cohesive. As much as you can avoid it, avoid any interdependcies between tests. If later you go back and delete tests, or add tests, you don’t want failures to crop up just because the order of execution changes based on assumptions of state you made from test to test. I might have a test class/suite called “EditModeTests” that goes through all the verification necessary for “Edit Mode” of the thing I’m building. Remember, at the end of the day, KIF tests are slow, so you don’t want a lot of redundancy between tests in terms of execution steps. So if you have the opportunity to perform verification and assertions on related items in a test, do it, as long as you aren’t totally sacrificing decoupling of that test from other tests. I know what you’re thinking, I’m proposing contradictory best practices. It’s all balance. You’ll feel it out as you go, I just wanted to bring up a couple things to be aware of. Remember, when a test fails, the best thing you can do to help yourself is to do everything possible to reduce the amount of time it takes to figure out why it failed. I see two easy ways to do this: ensure your tests don’t fail, and ensure that when your tests fail the context of why the test failed is clear.
KIFUITestActor Extension
KIFUITestActor
is the class of the tester()
available in KIFTestCase
s. It’s what you use to perform the navigation through your app. Don’t forget about extensions, they are a great way to add behavior to KIFUITestActor
, especially common pieces of code for repetitive navigatoin tasks. For example, one of my apps conditionally shows an onboarding flow depending if the user is launching the app for the first time or not. I added two methods to a KIFUITestActor
extension – one to check if the onboarding view was showing, and one method to close the onboarding flow if it was showing. This way, in all my KIF tests, I can reuse this code and have the confidence in the repeatability of the test. It’s KIF tips and tricks like this that make me really enjoy iOS functional testing.
Verify Something Is NOT On The Screen
KIF makes it really easy to verify that something IS on the screen, but there’s no obvious API for verifying something isn’t on the screen. You can use Swift’s do/try/catch to achieve this.
Consider this test:
func testPreviewIsNotAvailable() {
do {
try tester().tryFindingViewWithAccessibilityLabel("Preview")
XCTFail("Preview should not be found.")
} catch {
// Nothing to do here - a throw here is a success.
}
}
This test verifies that “Preview” is not available on the screen. KIF will throw an exception when it can’t find a view with the matching accessibility label after a 10 second timeout. That exception will be caught by the catch
handler, at which point nothing is done, and the test will pass. In the case that a view with a matching accessibility label IS found, the test is explicitly told to fail. If you use this pattern, I suggest a good comment in the empty catch
block so you help your future self and others understand what’s happening.
Wrap Up
I hope you find use of these KIF tips and tricks, and I hope that you are setup well to have success with your journey into iOS functional testing with KIF. This wraps up the week of KIF. I’d love to hear how you it works for you.
Happy cleaning.
Hi There, I am trying to run a single test case from terminal. I am using xcodebuild to run my KIF tests.