How to access the App Delegate from a UI Test? - ios

I want to access a particular property router from the App Delegate of the launched app during a UI Test, but I can't figure out if this is possible or not. I have tried:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let router = appDelegate.router
but this gives a failure and won't even build. I am using #testable on my project module. Any ideas?

Xcode UI testing is designed such that the test code can only see what a user can see, so no objects from the application under test can be used or inspected in the test code. This is also why the views on the screen are represented as XCUIElement objects instead of UIView descendants.
The UI tests run in a separate executable from the application under test. The only way to communicate additional information from the app to a UI test is by constructing a string containing the information and using it as the accessibility identifier for an element.
If you want to test something that requires access to an object from the application code, it is most likely that you need to write a unit test instead of a UI test.

Related

Understanding XCTestCase life cycle - How to perform setup BEFORE Xcode unit test launches the app?

I am new to unit tests in Xcode and Swift and have some trouble to understand the life cycle of XCTestCase.
How/where to add setup code which is executed before the actual app is launched?
Problem is, that first the host app is launched before any of the test setup methods (class func setUp(), func setUp(), func setUpWithError()) are executed.
It is even possible to run test code before the host app launches?
Details:
As described in a previous question my app uses a SQLite database to persist some data. When the app launches a database connection is created and data is read from the database.
To make tests consistent and repeatable I would like to use a fresh database with some well defined data every time a run the test. To archive this I tried to override setUpWithError remove the existing db file and move a file with pre-defined data in place instead.
Unfortunately this does not work, because setUpWithError is executed only after the host app was launched. The same is true for all other test setup methods.
Moving a fresh database file in place before running the tests is only an example. The problem is the same for all local data which should be in place before the host app launches to ensure repeatable tests.
An answer to my previous question included a UIApplication extension with a isTesting method which can be used to check if a test is performed. While I could use this in my app code to setup the test data, I would consider this a bad solution. I would like to keep the code completely separated from the production code. Is this possible?
There are several approaches to set up data before running a test case
NSPrincipalClass
As described here you can create a class, and the init method of that class is executed before running any test. This helps setting up dependencies used by many test cases. I don't think this is the way to go in your case.
isTesting
Instead of setting up the code in your app target, you can also check for isTesting early in the didFinishLaunchingWithOptions method of your AppDelegate and simply return there. In this case the regular code is not executed and you can run you custom database setup code in the setup of your test class.
Dependency Injection
Right now you are doing integration testing in my opinion. If you want to have proper unit tests, they should not operate against the database of the underlying app. Instead create an extra (in memory) database and inject that into the code you are testing. I think this is the way you should follow. The benefits are
Your app is not affected by your tests. Next time you start the app to manually test, your original data is still there.
Your unit tests are not affected by manual changes to the app
Your unit tests are also independent of each other. Order of execution doesn't change the results when setting up the database fresh for each test case.
It is even possible to run test code before the host app launches
If you have to ask that, your tests are not unit tests. Unit tests test code, not the app. A test that requires the app to be running would be a UI test.
In fact, the best approach for unit tests is to put all your testable code into a framework and give the framework unit tests; that way, your tests run much faster because the app never has to launch at all.
It sounds to me like the problem lies deeper in your app's code: you have evidently not written your code in such a way as to be testable. So that would be your first move. Writing testable code, and writing unit tests, is an art; you have to separate out the "system under test", which should be your code alone, and make sure you are not testing anything that doesn't belong to you and whose workings are already known — like Core Data.

Can an XCTestRunner access View Controller properties of target application?

I am trying to write some smart UI tests using XCUITest. Now I want to build a test suite such that, depending on my applications ViewController I make deduce what type of testing happens. Example would be I have a monkey test running in a loop, On some view controllers of my app I would like taps on the full screen, however on other view controllers i'd like taps only on a part of the screen, With what I've seen UI tests are bundeled and run in UITestRunner and my application being tested is the target application, Is there anyway I can access properties of View Controllers of the target applications ?
No, you can not access view controller properties at run time from UI test code because the UI test runner is a separate executable to the app (where the view controller would be accessible).
UI tests are high-level integration tests for testing functionality at the UI level. You can only give input via touch and only receive output via the UI.
If you need to test something that requires access to view controller properties, you need to use a unit test. Unit tests run in the same executable environment as the app so you can access particular properties of the objects you've made.
If you want to make the UI test interact with different pages differently, think about narrowing the list of available elements to tap, and identify particular pages to further filter the list by looking for an element unique to each page. Check out the page object model for an idea about how to scale your UI tests for multiple pages.
You can try '#testable import ' to access your app. but you may never get the reference to the currently running app while running XCUI Tests by doing this.
What you should consider is writing Integration tests which will run in the app context, then you can do as below:
#testable import
let win = UIApplication.shared.delegate?.window!
let rootViewController = win?.rootViewController as <>
and then access the rootViewController and send actions to the controller and controls defined in that particular controller.
We write most of our tests this way which is more easier, faster and reliable.
Refer to the Quick Documentation Below for more details.
https://github.com/Quick/Quick/blob/master/Documentation/en-us/TestingApps.md

How does framework like KIF or EarlGrey access the running application?

Usually in iOS unit tests, we create new objects, call the method we wanna test, and then validate the results. This is a stand-alone procedure. The test cases always start running with the app instance but we do not access that instance directly.
However, with the framework like KIF or EarlGrey, we are able to write functional tests by accessing UI elements with accessibility labels in the running app instance. I am wondering how it is implemented. We don't have something like context or root view controller object when tests start, how does the framework find the presenting view controller from "nowhere"?
Because they are based on XCTest's Unit Test paradigm. In it, the tests and the app are both in the same bundle and therefore have access to the app internals.
Using [UIApplication sharedApplication], you can actually get the UIWindow for the app and find the entire View Hierarchy.

During iOS application tests, how can I access a singleton instance in the host application?

I'm writing an iOS app with Swift, and we are using Parse for our backend. I'm working on some acceptance tests, and have mocked the Parse API using OHHTTPStubs.
I'm writing a test that taps through our login form and logs in a given user. In the view controller, we are calling the PFUser.logInWithUsernameInBackground function. Inside the completion block, I'm calling println(PFUser.currentUser()), and I can see that it is set correctly.
However, when I call println(PFUser.currentUser()) from my test case, it is still nil. I want to make sure that the user is being signed in, and that their username is correct.
The tests are application tests which are injected into the host application, and are written with KIF. So I think the tests may contain their own separate PFUser singleton instance, instead of accessing it on the host application. So I need to figure out how to gain access to the currentUser() inside the main app.
Is there any way I can tell my tests to use the PFUser class from the main app?
If you think this should normally work out of the box, what kind of mistakes should I look for in my code? (e.g. bridging headers, build settings, etc.)
I figured out the solution. I needed to access the class from the main bundle, and I did so by adding this line to a helper file in my test target:
let AppUser: AnyClass! = NSBundle.mainBundle().classNamed("PFUser")
This lets me perform checks such as AppUser.currentUser() == nil, and call functions like AppUser.signOut() on the main bundle, instead of in the test bundle.
The reason is that the main bundle and test bundle are separate, and classes are not shared between them. This OHHTTPStubs wiki page goes into more depth about the difference between hosted and non-hosted tests in Xcode.

Accessing the current activity when running an NunitLite test

I've got an ISettings interface which is implemented in a platform-specific manner on Mono For Android and iOS. For Mono For Android, I'm using Activity.GetSharedPreferences to implement my settings - and this works fine from my application.
However, I want to be able to test this with NunitLite (which I'm using successfully to test my other core code).
The problem is that for my test fixture, I need an Activity on which to call GetSharedPreferences. Is there a way of accessing the currently running Activity either generically or via the NunitLite implementation?
James
You can access Shared preferences via:
Application.Context.GetSharedPreferences("whatever", FileCreationMode.Private)
You don't need an activity.

Resources