foens
foens

Reputation: 8672

Calabash: How to set up test data

I am in the process of implementing GUI tests using Calabash. In The Cucumber Book it is emphasized that scenarios should setup test data itself, locally, preferably using libraries such as factory_girl. This question is about the performance, cleanness and manageability of testing Android and iOS applications using Calabash.

The benefit of tools such as factory_girl is that tests should tend to be less brittle and that the test data can be created/inserted without using the GUI, which speeds up the tests considerably (which makes them worth more to the developers). Also, each scenario should be independent from all other scenarios, such that it does not require scenario A to be run before B if B is to work correctly. This allows developers to run only a single scenario.

This seems reasonable for programs that is running locally, where for example the web-service database can be accessed directly, such that test data can be inserted. However, how does a tester insert data in programs that run on another device (emulator, simulator, real phone).

Specifically, how does one manage test data probably when running against an iOS and Android target? I am tempted to use a Set of Fixture Data as described in The Cucumber Book, however, they explicitly says to avoid this.

I ask because the app I am creating has a lot of setup before the user can enter the main app view. The user needs to:

  1. Sign up, which consists of multiple steps:

    A. Press "Sign up"

    B. Accept terms

    C. Link to 3rd party service (multiple steps)

    D. Enter user details (name, ...)

    E. Press the "Sign up" button

    F. Confirming email by pressing link in sent email

  2. Log in with the newly created user

  3. Synchronize data with the server

As you can see, if each scenario has to work from a clean state, then just getting to the correct view in the application can be a real time consumer. Those steps has to be executed for nearly all scenarios. I would like to be able to start with the required state and start at the correct view. How is this achieved for iOS and Android using Calabash?

Upvotes: 3

Views: 1446

Answers (3)

Austin
Austin

Reputation: 189

We were able to create our own test data by using the 'Rest-Client’ gem (to call endpoints) and Cucumber hooks (used to determine when to generate test data).

See below example of how we created new accounts/customers by using the Rest-Client gem, cucumber hooks, a data manager class and a factory module. Here's a link with a bit more info on how it works.

AccountDataManager.rb

require 'rest-client'

require_relative '../factory/account'

class AccountDataManager

  include Account

  def create
    current_time = Time.now.to_i
    username = 'test_acc_' + current_time.to_s
    password = 'password1'
    url = 'http://yourURLhere.com/account/new'

    request_body = manufacture_account(username, password)

    response = RestClient.post url, request_body.to_json, {:content_type => 'application/json', :accept => 'application/json'}

    if response.code != 200
      fail(msg ="POST failed. Response status code was: '#{response.code}'")
    end

    response_body = JSON.parse(response

    clientId = response_body['Account']['ClientId']

    # return a hash of account details
    account_details = {
        username: username
        password: password,
        clientId: clientId
    }
  end    
end

Account.rb

The below factory manufactures the request's body.

module Account

  def manufacture_account(username, password)
    payload = {
        address:{
            :Address1 => '2 Main St',
            :Address2 => '',
            :Suburb => 'Sydney',
            :CountryCode => 8
        },
        personal:{
            :Title => 'Mr',
            :Firstname => 'John',
            :Surname => 'Doe',
            :UserName => "#{username}",
            :Password => "#{password}",
            :Mobile => '0123456789',
            :Email => "#{username}@yopmail.com",
            :DOB => '1990-12-31 00:00:00'
        }
    }
  end
end

Hook.rb

You should add your hook.rb file to a shared directory and then add a reference to it into your env.rb file (we added our hook file to the "/features/support" directory).

require_relative '../data_manager/data_manager_account'

Before() do
  $first_time_setup ||= false

  unless $first_time_setup
    $first_time_setup = true

    # call the data managers needed to create test data before
    # any of your calabash scenarios start
  end
end

Before('@login') do
  # declare global variable that can be accessed by step_definition files
  $account_details = AccountDataManager.new.create
end

at_exit do
  # call the data managers to clean up test data
end

Login_steps.rb

The final piece of the jigsaw is to get your calabash scenarios to consume the test data your've just generated. To solve this problem we declared a global variable ($account_details) in our hook.rb file and referenced it in our step_definition file.

Given(/^I log in with newly created customer$/) do
  @current_page = @current_page.touch_login_button

  unless @current_page.is_a?(LoginPage)
    raise "Expected Login page, but found #{@current_page}"
  end

  # use global variable declared in hook.rb
  @current_page = @current_page.login($account_details)

 unless @current_page.is_a?(HomePage)
    raise "Expected Home page, but found #{@current_page}"
  end
end

Upvotes: 1

bfarache
bfarache

Reputation: 1

You can write a Backdoor to clean up your data before running tests:

public class MainActivity extends Activity {

    public void setUp() {
        // Here you can clean and setup the data needed by your tests
   }

}

Upvotes: 0

lux
lux

Reputation: 8436

I haven't gotten into using any fixture data libraries myself, however I've had luck just using simple models, like the following for a User.

class User

  @validUser = "[email protected]"
  @validPass = "123"

  @invalidUser = "[email protected]"     
  @invalidPass = "foobar"

  def initialize(username, password)
    @username = username
    @password = password
  end

  def username
    @username
  end

  def password
    @password
  end

  def self.getValidUser
    return new(@validUser, @validPass)
  end

  def self.getInvalidUser
    return new(@invalidUser, @invalidPass)
  end

end 

Let's say I have the following feature:

Scenario: Successful login
  When I enter valid credentials into the login form
  Then I should see a logout button

Then when I need a valid user, it's as easy as:

When(/^I enter valid credentials into the login form$/) do
  user = User.getValidUser
  enterCredentials(user)
end

And you can obviously replicate this model structure for anything than needs to hold a simple amount of information. Again, I can't speak for any fixture libraries as I haven't utilized those.

However, to your other question regarding the non-dependence of Scenario A on Scenario B - this is true, but this does not mean you can't string together step definitions to achieve your goals. Let's say the test above was simply used to validate that I can successfully log in to the application with a valid user - all is well, the user is logged in, and everyone's happy. But what happens when I need to test the profile page of a user? Well I obviously need to login, but I don't need that to be explicitly stated in my feature file, I can create a step definition that chains together other step definitions:

Scenario: Validate behavior of user profile page
  Given I'm on my profile page
  Then I should see a selfie


Given(/^I'm on my profile page$/) do
  step "I enter valid credentials into the login form"
  navigateToProfilePage()
end

This is a quick example, but hopefully you're seeing that you can indeed chain together step definitions without having scenarios themselves being depending on one another. Such a bad style of cucumber would be such that you log in to the application with Scenario "Login Successful" but then NEVER log the user out, and simply proceed with Scenario "Validate behavior of profile" which just navigates to the profile page without first logging back in. I hope I wasn't too far off from your original request!

Upvotes: 0

Related Questions