Skip to content

Instantly share code, notes, and snippets.

@AdamMc331
Created April 5, 2018 02:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AdamMc331/8beb16c73d7deefe870fc25947728bf1 to your computer and use it in GitHub Desktop.
Save AdamMc331/8beb16c73d7deefe870fc25947728bf1 to your computer and use it in GitHub Desktop.
Small gist used to answer a followup question from the NY Android Meetup.
// Any actions that occur on the Registration screen, successful or otherwise, should only deal with the RegistrationRobot.
// If you move to a new screen, the robot itself doesn't need to be aware of that. Your test can create the new robot.
// As seen in this example, where we move the flow over to a ProfileRobot after clicking register.
@Test
fun testSuccessfulRegistration() {
RegistrationRobot()
.firstName("Adam")
.lastName("McNeilly")
.email("amcneilly@okcupid.com")
.phone("1234567890")
.register()
ProfileRobot()
.assertFullNameDisplay("Adam McNeilly")
.assertEmailDisplay("amcneilly@okcupid.com")
.assertPhoneDisplay("(123)-456-7890")
}
// Following the above comment, we can see that we don't have to worry now when we want to test a negative case, because
// that sort of thing is still handled by the RegistrationRobot.
@Test
fun testMissingEmailError() {
RegistrationRobot()
.firstName("Adam")
.lastName("McNeilly")
.phone("1234567890")
.register()
.assertEmailError("Must enter an email address.")
}
@bhargman
Copy link

bhargman commented Apr 5, 2018

Thanks for the follow up Adam, it seems like there are three ways to approach error assertions:

  • Defined by the robot itself (like the example above, named with context, e.g., assertEmailError())
  • Within a base open class that all robots extend from (generically named, e.g., assertError(textViewId, errorText))
  • Delegated to a reusable ErrorRobot (generically named, e.g., assertError(editTextId, errorText)):

I'm sort of leaning towards the last b/c errors are interruptions in the UI, and we can represent that in the code as well since it interrupts the flow of the robot's actions:

@Test
fun testSuccessfulRegistration() {
    RegistrationRobot()
            .firstName("Adam")
            .lastName("McNeilly")
            .email("amcneilly@okcupid.com")
            .phone("1234567890")
            .register() // this now returns a ProfileRobot(), flow continues nicely
            .assertFullNameDisplay("Adam McNeilly")
            .assertEmailDisplay("amcneilly@okcupid.com")
            .assertPhoneDisplay("(123)-456-7890")
}

@Test
fun testMissingEmailError() {
    RegistrationRobot()
            .firstName("Adam")
            .lastName("McNeilly")
            .phone("1234567890")
            .register() // ignore returned ProfileRobot(), this flow has been interrupted

    ErrorRobot()
            .assertError(R.id.etEmail, "Must enter an email address.")
}

I guess they each have tradeoffs, and it depends on the app's UX as well.

@bhargman
Copy link

bhargman commented Apr 6, 2018

Actually, attempting method 3 in a larger application quickly shows its downfalls. There's no guarantee that a register() type of method that transitions to another robot always returns the same robot. I'll stick to the method you mentioned, thanks again! Some lessons learned:

  • Role of a test:
    • Uses robot(s) to verify some sort of app behavior
    • Can setup seed data (either mocks or real data) in order for the robots to perform their actions
  • Role of a robot:
    • Performs I/O actions for a specific screen
    • Performs assertions for a specific screen's views
    • Does not know about any other robots

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment