Access to project code from XCTestCase - UI Test
Asked Answered
R

1

10

I'm trying to setup UI Test in my project. I'm making a UI test that tries to login through my apps login prompt. In order to ensure that the login prompt is shown when the test launches, I'm trying to run ServerManager.logout(), which is in the project's codebase. This will cause the Login prompt to be shown on launch.

import XCTest

class SmokeTest: XCTestCase {

    override func setUp() {
        super.setUp()

        // Put setup code here. This method is called before the invocation of each test method in the class.

        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false
        // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
        XCUIApplication().launch()

        // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
        ServerManager.logout()
    }

    func testLogin() {

        let app = XCUIApplication()

        let emailTextField = app.textFields["email textfield"]
        emailTextField.tap()
        emailTextField.typeText("[email protected]")

        let passwordTextField = app.secureTextFields["password textfield"]
        passwordTextField.tap()
        passwordTextField.typeText("12345678")

        let loginButton = app.buttons["log in button"]
        loginButton.tap()
    }
}

How should I set up my project to get access to ServerManager?

When I checked on Target Membership for UITest target (called DonkeyProductionUITests) on the ServerManager.swift file, Xcode started complaining that many reference in that file was undefined for the UITest target. So I added Target Membership for all files in my project to the UITest Target including all CocoaPods. It solved most issues. Still I have some weird leftovers:

Undefined reference

What source files should the UITest target have as "Compile Sources"?

Why are types like UIColor and UIViewController suddenly not recognizer by Xcode?

Rapeseed answered 13/2, 2017 at 10:31 Comment(0)
G
19

Accessing your project's code via @testable import is only possible in UnitTests. When you are running UITests this is not working, because during a UITest your test class cannot access your app's code.

From Apple's Docs:

UI testing differs from unit testing in fundamental ways. Unit testing enables you to work within your app's scope and allows you to exercise functions and methods with full access to your app's variables and state. UI testing exercises your app's UI in the same way that users do without access to your app's internal methods, functions, and variables. This enables your tests to see the app the same way a user does, exposing UI problems that users encounter.

If you want to logout after a test you have to do it via your app's User Interface: If there is a logout button somewhere in your app, navigate there at the end of your test and let the test tap() it.

Ghirlandaio answered 13/2, 2017 at 18:14 Comment(8)
Awesome, thanks! But slightly annoying.. I simply want to make sure the app is in then right state when the test is launched, but that seems not to be the use case for func setup()... Would you mind adding the link to the reference from apple?Rapeseed
Yes, it's a bit inconvenient, but there is no way around it. You have to use your app's UI to reset your after a test. If you need to do that after each test, you could move that code to tearDown(). Here is the link to the docs: developer.apple.com/library/content/documentation/…Ghirlandaio
Don't you think it is a solution to launch the app with arguments, like this solution: https://mcmap.net/q/176630/-accessing-the-host-app-code-from-the-xcode-7-ui-test-target Thanks :)Rapeseed
Yes, you could pass arguments from the UITest to your app, but then you have to put code inside your app that checks if the arguments are there and then logs out the user without any user interaction. You could do that, but IMHO it's a bit dirty. I also use environment variables in my UITests but only to mock existing behavior inside the app (like mocking failed API calls). And you probably have to UITest the Logout button anyway, right?Ghirlandaio
I'm confused how can you mock your code—while you "cannot access your app's code."Joella
Pass a launch argument from UITest class to your app -> In your app check if the launch argument is there. If it is, connect your app to a mocked API. If it is not there, connect your app to the real API. I also pass the behavior of the mocked API (e.g. if it should return an error)Ghirlandaio
You could also create a custom view (table/collection view) for all the states in your app you'd want to test and then make the UI test select the state for every test in the UI itself. You could make this view a hidden feature only accessible while testing.Innis
This makes no sense so now its expected to add more complexity and if then else if your running UITests. This makes UI Tests pointlessBeadruby

© 2022 - 2024 — McMap. All rights reserved.