Xcode UI testing - login/logout with stored creden

2020-07-10 19:55发布

问题:

I want to run functional (UI) tests for the login procedure in my iOS app (Xcode 7.2.1).

The app's behaviour is that upon successful login, user credentials are stored in order to automatically login (without showing the login screen) in the next launches.

So I set up a sequence of UI events in the login screen to make the login test pass on the first time the app launches in the iOS Simulator. However, next times I run my tests will fail, since the login screen doesn't even show up as expected.

I see two options here, none of them seem to fit well:

  1. Reset iOS Simulator's content and settings with a script before each time my tests run. I tried adding a Run Script phase in the test target's Build Phases with: xcrun simctl shutdown booted && xcrun simctl erase all && killall "Simulator", and it doesn't seem to work (Simulator app doesn't launch and tests get stuck).
  2. Include in the -(void)tearDown some code to clear the stored user credentials. This option is not good either as not only it's run between each test method (not per test launch), but also it seems like I don't have access to the AuthManager class that I use to clear user's credentials.

What do you do when UI-testing login procedures like that?

回答1:

I've ran into the same types of issues. After a bunch of bashing around my best approach was to try and keep things a little more simple. In my tear downs I always "unwind" anything that I might have done. Some times it's overkill but it's still good practice. I've found many bugs while unwinding where I might have overlooked them if I did some sort of hard reset. IE: I'll navigate back to the home page (my starting point) and if I've signed in then I'll simply just sign out. For the record, on the app side when a users signs out, their credentials are stripped.

So for example, on my SignInTests.swift classes I put all my methods in an extension in the same class file. That way I can simply call SignInTests().signIn() or SignInTests().signOut() so that I can access them from whatever other test class I might to called signOut() from.

This is my scenario that works perfectly for me. May not be the best option for you but I hope it points you in the right direction.



回答2:

Our answer (at my company) was to create an extension of XCTestCase called ContainerResettingUITest. We pass in a series of additional launch arguments that override setUp (rather than tearDown) and then in main() if those launch arguments were passed in we nuke everything in the docs directory, the keychain, and the nsuserdefaults.

A somewhat nuclear option. Don't pass that launch argument by accident.

Hopefully that answers your question; I use it for exactly the same reason you seem to want it, to have totally uncontaminated sign-in and sign-up tests.



回答3:

When you run your application from XCTestCase you could use something like this

let app = XCUIApplication()
app.launchArguments.append("--uitesting")
app.launch()

And in AppDelegate method

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool

if CommandLine.arguments.contains("--uitesting") {
    clear()
} 

Run this in first test when you need to login. In next test you could clear data from launchArguments.

app.launchArguments = []