Behave (BDD) with Appium for iOS/Android

What are we going to do?

Implement Behave (BDD) on top of Appium.

Behave is useful for describing functionality of your product and shines because you only have to describe it once and run it on several platforms.

But why?

Writing your tests in Gherkin language provides several benefits:

  • Describes in human readable form what a feature should do
  • Closes the gap between Product, Manual Testers and Test Engineering, have your product manager or your manual testers write the tests!
  • Serves as documentation, want to know everything that this feature does? Read the feature files!

The main purpose is to write a test and describe its workflow without any platform dependent specifications, here is an example:

Feature: Log in
  As a user I want to be able to login with my valid username and password

  Scenario: Log in with valid credentials
    Given The app is ready
    Then I enter my username "teddy@gmail.com"
    And I enter my password "bonkers"
    And I tap the Sign in button
    Then I verify that I am logged in

The code above is defined as a feature file, It is very easy to read and it does not have any platform implications.

So how do we make it work for iOS and Android?

First of all lets install Behave, add this to your ‘requirements.txt’ if you do not have one, consider creating one, it will make handling your python package dependencies way easier, more information here.

behave==1.2.5

Then run:

pip install -r requirements.txt

Now lets use the feature file described above, name it login.feature.

The directory structure that we are going to use will be the following:

Screen Shot 2017-04-10 at 6.11.11 PM

We have a ‘controller’ directory that will hold controllers for both iOS and Android, controllers will define how we actually interact with the application, here is where the differentiation between platforms is made.

A ‘features’ directory that will contain all of our feature definitions.

A ‘steps’ directory that will contain the translations from Gherkin language to actual code steps.

Now, lets write the contents of login_steps.py:

The way that behave know what to do for each step is by matching to a function with a decorator:

In our feature file we have the following step:

Then I enter my username "teddy@gmail.com"

That will get translated to:

from behave import *

@step('I enter my username "{email}"')
def login(context, email):
    # TODO: Call the login controller to input the email

But how can we make sure that is using the right controller?

Behave allows us to specify user-defined variables as command line arguments, so lets send the platform name:

behave -D profile=android

We can retrieve this variable by accessing context.config.userdata.get('profile')

We will also use this information to instantiate the proper web driver:

if context.config.userdata.get('profile') == 'ios':
    self.driver = webdriver.Remote(settings.APPIUM_SERVER['url'],
                                   settings.DRIVER_CAPABILITIES['IOS'])
elif context.config.userdata.get('profile') == 'android':
    self.driver = webdriver.Remote(settings.APPIUM_SERVER['url'],
                                   settings.DRIVER_CAPABILITIES['ANDROID'])

Now, it makes sense to have an entity that takes care of loading the controllers as well, lets call this controller_manager:

class ControllerManager:
    login_controller = None
 
    def __init__(self, driver, platform):
       if platform == 'ios':
            from controllers.ios.login_controller import LoginController
        elif platform == 'android':
            from controllers.android.login_controller import LoginController
        else:
            raise RuntimeError('Unrecognized platform: {}'.format(platform))
        self.login_controller = LoginController(self.driver)

And for the controller code:

class LoginController:
    LOCATORS = {
        'email_text_field': 'email'
    }

    def input_email(self, email):
        self.driver.find_element_by_id(self.LOCATORS['email_text_field']).input_text(email)

So lets go back to the step definition and complete it:

@step('I enter my username "{email}"')
def login(context, email):
    controller_manager = ControllerManager(context.config.userdata.get('profile'))
    controller_manager.login_controller.input_email(email)

So to summarize here is what happens:

  1. Test run starts, with a specific platform selected for example android
  2. Controller Manager gets initialized loading the login_controller for android and instantiating it.
  3. A step gets executed in the feature file.
  4. The step definition calls the method input_email and properly handles the interaction with the app.

So thats it! there is definitely room for improvement, for example: the controller manager should be initialized somewhere else, not in the actual call for a method.

Advertisements

2 thoughts on “Behave (BDD) with Appium for iOS/Android”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s