Tutorial#

For this tutorial, we will visit GitHub, execute a search for EdX’s version of its open source MOOC platform, and verify the results returned.

Folder structure#

Your test will be a Python module, so let’s get started by defining it as such. Make a folder for your project, and inside that create an empty file named __init__.py.

/home/user/bok-choy-tutorial
    - __init__.py
mkdir ~/bok-choy-tutorial
cd ~/bok-choy-tutorial
touch __init__.py

Round 1 - The framework of a test#

Let’s set up and execute a simple test to make sure that all the pieces are installed and working properly.

Define the page#

The first step is to define the page object for the page of the web application that you will be interacting with. This includes the name of the page and a method to check whether the browser is on the page. If it is possible to navigate directly to the page, we want to tell the page object how to do that too.

Create a file named pages.py in your project folder and define the GitHubSearchPage page object as follows:

/home/user/bok-choy-tutorial
    - __init__.py
    - pages.py
from bok_choy.page_object import PageObject


class GitHubSearchPage(PageObject):
    """
    GitHub's search page
    """

    url = 'http://www.github.com/search'

    def is_browser_on_page(self):
        return 'search' in self.browser.title.lower()

Write a test for the page#

Write the first test, which will open up a browser, navigate to the page we just defined, and verify that we got there.

Create a file named test_search.py in your project folder and use it to visit the page as follows:

/home/user/bok-choy-tutorial
    - __init__.py
    - pages.py
    - test_search.py
import unittest
from bok_choy.web_app_test import WebAppTest
from pages import GitHubSearchPage


class TestGitHub(WebAppTest):
    """
    Tests for the GitHub site.
    """

    def test_page_existence(self):
        """
        Make sure that the page is accessible.
        """
        GitHubSearchPage(self.browser).visit()


if __name__ == '__main__':
    unittest.main()

Execute the test#

Execute the test from the command line with the following.

python test_search.py
.
----------------------------------------------------------------------
Ran 1 test in 3.417s

OK

What just happened?#

You should have seen your default browser launch and navigate to the GitHub search page. It knew how to get there because of the page object’s ‘url’ property.

Once the browser navigated to the page, it knew it was on the right page because the page’s ‘is_browser_on_page’ method returned True.

Round 2 - Interacting with a page#

Let’s circle back around to improve the definition of the page and have the test do something more interesting, like searching for something.

Improve the page definition#

Tip

A Best Practice for Bok Choy tests is to use css locators to identify objects.

Hint

Get to know how to use the developer tools for your favorite browser. Here are links to articles to get you started with Chrome and Firefox.

Edit your pages.py file to add in the input field where you type in text and the search button. Using the Developer Tools for my browser, I see that the input field can be identified by combining form tags id (#search_form) and input tags type (text), so its css locator would be ‘#search_form > input[type=”text”]’.

<form accept-charset="UTF-8" action="/search" class="search_repos" id="search_form" method="get">
    <input type="text" data-hotkey="s" name="q" placeholder="Search GitHub" tabindex="1" autocapitalize="off" autofocus="" autocomplete="off" spellcheck="false">

Add a method for filling in the search term to the page object definition like this:

        """
        Fill the text into the input field
        """
        self.q(css='input[type="text"][aria-label="Search GitHub"]').fill(text)

What’s next? I see that type (button) and class (button) are good way to identify the search button. Its css locator would be “button.button”.

<button class="button" type="submit" tabindex="3">Search</button>

We will need to define how to press the button. But we also want to define how we know that pressing the button really worked. Try it yourself in a browser. While I’m writing this tutorial, the way the GitHub search currently works is to bring you to a search results page (as long as you entered text into the input field).

So before we add the method for clicking the Search button, we should add the definition for the search results page to pages.py. If we want to use the page title again, we can see that when you search for “foo bar” it will be:

<title>Search · foo bar</title>

Add another page’s definition#

So we add the search results page definition to pages.py:

import re
from bok_choy.page_object import PageObject


class GitHubSearchResultsPage(PageObject):
    """
    GitHub's search results page
    """

    # You do not navigate to this page directly
    url = None

    def is_browser_on_page(self):
        # This should be something like: u'Search · foo bar · GitHub'
        title = self.browser.title
        matches = re.match('^Search .+ GitHub$', title)
        return matches is not None

Define the search method#

Back to defining a method for pressing the button and knowing that you have arrived at the results page: We want to press the button, then wait and make sure that you have arrived at the results page before continuing on. Page objects in Bok Choy have a wait_for_page method that does just that.

Let’s see how the method definition for pressing the search button would look.

    """
    GitHub's search page
    """

    url = 'http://www.github.com/search'

    def is_browser_on_page(self):
        return 'search' in self.browser.title.lower()

    def enter_search_terms(self, text):
        """
        Fill the text into the input field
        """
        self.q(css='input[type="text"][aria-label="Search GitHub"]').fill(text)

    def search(self):
        """
        Click on the Search button and wait for the
        results page to be displayed
        """
        self.q(css='input[type="text"][aria-label="Search GitHub"]').click()
        GitHubSearchResultsPage(self.browser).wait_for_page()

    def search_for_terms(self, text):
        """
        Fill in the search terms and click the
        Search button
        """
        self.enter_search_terms(text)
        self.search()

Add the new test#

Now let’s add the new test to test_search.py:

import unittest
from bok_choy.web_app_test import WebAppTest
from pages import GitHubSearchPage, GitHubSearchResultsPage

class TestGitHub(WebAppTest):
    """
    Tests for the GitHub site.
    """

    def setUp(self):
        """
        Instantiate the page object.
        """
        super().setUp()
        self.github_search_page = GitHubSearchPage(self.browser)

    def test_page_existence(self):
        """
        Make sure that the page is accessible.
        """
        self.github_search_page.visit()

    def test_search(self):
        """
        Make sure that you can search for something.
        """
        self.github_search_page.visit().search_for_terms('user:openedx repo:edx-platform')


if __name__ == '__main__':
    unittest.main()

Run it!#

python test_search.py
..
----------------------------------------------------------------------
Ran 2 tests in 8.478s

OK

What just happened?#

The first test ran, just as before. Now the second test ran too: it entered the search term, hit the search button, and verified that it got to the results page.

Round 3 - Search and verify results#

In the test version that we just completed we entered some search terms and then verified that we got to the right page, but not that the correct results were returned. Let’s improve our test to verify the search results.

Improve the page definitions#

Since we want to verify the results of the search, we need to add a property for the results returned to the page object for the search results page.

Also maybe we want a better way to determine that we are on the search page than just the words “code search” the title. Let’s use a query to make sure that the search button exists.

Improve the search test#

Now we want to verify that edx-platform repo for the EdX account was returned in the search results. And not only that, but also that it was the first result. Modify the test_search.py file to do these assertions:

Run it!#

python test_search.py
..
----------------------------------------------------------------------
Ran 2 tests in 7.692s

OK

What just happened?#

Both tests ran. We verified that we could get to the GitHub search page, then we searched for the EdX user’s edx-platform repo and verified that it was the first result returned.

Take it from here!#

This tutorial should have gotten you going with defining page objects for a web application and how to start to write tests against the app. Now it’s up to you to take it from here and start testing your own web application. Have fun!