Selenium and REST API Testing with Gauge

 

I had a requirement to test a server that provided a browser UI as well as REST APIs used by the browser and by a mobile application.

My test scenario covered an end-to-end flow that required a user accessing both the browser and using a mobile application in the workflow.  I needed a tool that could let me test using selenium and calling the APIs in the same test.

The following shows an example of how I set this up in Gauge (using Python).  This example uses Magento as the test target.

Gauge is a light weight, open-source, cross-platform test automation tool. This example uses the Python variant.

Magento is an open-source, e-commerce platform written in PHP. It has both a browser interface and Rest API’s so makes a good example target environment for this scenario.

Reasons for testing Rest API’s instead of all selenium are for speed and to simulate the mobile application and test the server independently.

Setup Gauge and Python using the instructions here: gauge-python.readthedocs.io

This example also uses the Python requests  HTTP library for API calls

and Hamcrest python library for validating results.

Gauge specifications are written in human readable markdown.  Each specification can contain multiple scenarios. Scenarios can contain multiple steps.

This scenarios covers a shopper, Ann setting up a new account for herself in the browser and then using a mobile application to login with her new account, put an item in her cart and submit the order. The admin then logs in with a browser and verifies they can see Ann’s order.

order-from-app.spec:

 Magento Order
=====================

Create a new customer
--------------------------------------

* Create customer account for "Ann" "Smith" with "ann@test.com"

Login from App
--------------------------------------

* Login from app as user "ann@test.com"

Add Products to Cart
--------------------------------------

* Create a cart from app as user "ann@test.com"
* Add item with sku "24-WB04" to cart from app as user "ann@test.com"

Payment and Order
--------------------------------------

* Process and submit order for cart from app as user "ann@test.com"
with firstname="Ann" lastname="Smith" street="1 Street" city="Brisbane"
region="QLD" postcode="1234" country="AU" telephone="55-555-55"

Verify Order as Admin
--------------------------------------

*  Login as Admin
*  Check order exists for user "ann@test.com"
*  Verify order contains item with sku "24-WB04"

Logout and Cleanup
--------------------------------------

* User "ann@test.com" closes browser
* User "admin" closes browser

 

Steps are prefixed with a *

Steps map to a python function. The step implementations can be structured in any way you like.

In this example I am passing the test data in as simple parameters but Gauge provides support to pass test data in as inline tables or files.

User Selenium Test Steps

The following shows the implementation of the first step to create the customer account.

customer.py:

@step('Create customer account for <first_name> <last_name> with <email>')
def create_new_customer(firstname, lastname, email):
    driver = BrowserFactory.getWebdriver()
    DataStoreFactory.spec_data_store().put(email + '_driver', driver)
    signup_as(driver, firstname, lastname, email, password="passw0rd")
    msg = WebDriverWait(driver, 10)
        .until(lambda driver: driver.find_element(By.CLASS_NAME, 'messages').text)
    assert_that(msg, any_of(contains_string("Thank you for registering"),
        contains_string("There is already an account with this email address")))

This step creates the web driver and saves a link to it using the Gauge provided DataStoreFactory, which provides scenario, spec, and suite level data stores.  This allows using the same browser instance in other specs if required or for multiple users in the same specification to have separate browser instances.

User REST API Test Steps

The following shows the step implementation for the user logging in from a mobile application (note that in this test we are using the API calls the mobile application would make to the server).

customer.py:

@step('Login from app as user <email>')
def get_token_for_user(email):
    appClient = MobileAppClient(email=email)
    DataStoreFactory.spec_data_store().put(email + '_app_client', appClient)
    appClient.request_token(password='passw0rd')

Mobile applications typically use an access token to access the users data on a server instead of passing the users password every time.  This means the mobile application is not required to store the users password.  This step is creating an instance of the MobileAppClient and storing it in the data store so subsequent steps can access the saved state of the mobile application.

MobileApp.py:

 class MobileAppClient(object):
    def __init__(self, email="", username="", authzn_code=None, access_token=None):
        """Create a REST Client to simulate a mobile app."""
        self.email = email
        self.username = username
        self.access_token = access_token
        self.authzn_code = authzn_code
        self.cart_id = ""
        self.order_id = ""

 

The step then requests an access token and saves it to the app client for the user.

MobileApp.py:

 def request_token(self, password):
        json = {'username': self.email, 'password': password}
        response = self._apiRequest(method='POST', endpoint='/index.php/rest/V1/integration/customer/token', jsonObj=json)
        json = response.json()
        self.access_token = json

 

In a subsequent step we then call the API that adds an item to the customer’s shopping cart.

customer.py:

@step('Add item with sku <sku> to cart from app as user <email>')
def add_cart_items(sku, email):
    appClient = DataStoreFactory.spec_data_store().get(email + '_app_client')
    appClient.add_item_to_cart(sku)

MobileApp.py:

   def add_item_to_cart(self, sku):
        json = {"cart_item": {"quote_id": self.cart_id,"sku": sku,"qty": 1}}
        response = self._apiRequest(method='POST', endpoint='/rest/V1/carts/mine/items', jsonObj=json)

 

Admin Selenium Test Steps

After the order is submitted the admin should be able to view the order using a browser. The following step shows checking the order is visible to the admin user.

admin.py:

@step('Check order exists for user <email>')
def check_order_exists(email):
    appClient = DataStoreFactory.spec_data_store().get(email + '_app_client')
    order_id = appClient.get_order_id()
    driver = DataStoreFactory.spec_data_store().get('admin_driver')
    navigate_to_orders_page_with_id(driver, order_id)

    order_block = WebDriverWait(driver, 10).until(lambda driver: driver.find_element(By.CLASS_NAME, 'order-view-account-information').text)
    assert_that(order_block, contains_string("Order & Account Information"))

This step accesses the app client to get the order id.  It then gets the admin’s browser instance and navigates to the order details page.

orders_page.py:

def navigate_to_orders_page_with_id(driver, id):
    # Messages.write_message("Getting URL: {0}".format(self.URL))
    MAGENTO_URL = '{}/admin/sales/order/view/order_id/{}/'.format(os.getenv('MAGENTO_URL'), id)
    Messages.write_message('Navigation URL: {}'.format(MAGENTO_URL))
    driver.get(MAGENTO_URL)

 

Logging

Gauge provides a Custom messages library that allows you to write to the execution report.

This is useful for logging the API requests and responses, and debugging failures.

Below shows the method for logging the API response.

MobileApp.py:

 def _log_response(self, res):
        Messages.write_message('\n---Response---')
        Messages.write_message('<i>Status code:</i> <b>{}</b>'.format(res.status_code))
        Messages.write_message('\n<i>Response Headers:</i>')
        Messages.write_message('\n'.join('{}: {}'.format(k, v) for k, v in res.headers.items()))
        if res.status_code != requests.codes.no_content:
            Messages.write_message('\n<i>Response Body:</i>')
            Messages.write_message('{}'.format(res.json()))

 

Report

As well as command line output gauge also creates nice html reports as shown below:

 

gauge-test-results

 

The full example is in github here: gauge-magento-test

 

Advertisements

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