Object Repository

The Object Repository contains all of the Page Objects that you have defined in your test-pack.

images/object-repository.png

The Object Repository serves two purposes:

  • Documentation when you are writing test scripts: It shows the Page Objects that you can use in your test, and their available properties and methods.
  • Debugging tool when you are writing new Page Objects.

What is a Page Object?

A Page Object is a Python class that uses Stb-tester APIs like stbt.match() and stbt.ocr() to extract information from the screen, and it provides a higher-level API in the vocabulary and user-facing concepts of your own application.

images/page-object-pattern.png

Based on Martin Fowler’s PageObject diagram

Once you have defined the appropriate Page Objects, your testcases can talk about user-facing concepts like “guide”, “menu”, and “programme title”. All the low-level details like image-matching, OCR, and regions, should be encapsulated inside Page Objects. This is a well-known pattern used in web-testing frameworks like Selenium.

If your testcase is calling stbt.match() or stbt.ocr() directly, you are Doing It Wrong!

—Paraphrasing Simon Stewart, from the Selenium project.

There are many advantages to using Page Objects:

  • Maintenance is easier and cheaper. If your GUI changes in appearance, you only need to update your test-pack in one place (the Page Objects), not in hundreds of different testcases.
    • Stb-tester provides special tooling that makes it easier to adapt your Page Objects when the GUI-under-test changes; you’ll see more about this below.
  • Clarity: Your testcases are shorter, and the intent of each testcase is clearer.
  • Documentation and code re-use: Stb-tester’s Object Repository shows you the relevant Page Objects for any particular screen. This makes it easier to use Page Objects written by other team members.

What is a Frame Object?

stbt.FrameObject is Stb-tester’s implementation of the Page Object pattern. It is a base class for the Page Objects that you define.

Stb-tester uses a separate instance of your class for each frame of video that is captured from the device-under-test (hence the name “Frame Object”). Each instance represents information about a specific frame of video — in other words, an instance of a FrameObject represents the state of the device-under-test at a specific point in time.

See stbt.FrameObject in Stb-tester’s Python API documentation for details about the special behaviour that FrameObject adds to your classes.

images/class-vs-instance.png

Each instance is a separate object; the class defines how the object behaves (what properties it has, etc.)

Creating new Page Objects

Here is the recommended workflow for developing your Page Objects:

  1. Save screenshots of the relevant screens under selftest/screenshots/.
  2. Create a class that derives from stbt.FrameObject.
  3. Define a property called is_visible.
  4. Define any other properties for information that you want to extract from the screen.
  5. Make a git snapshot of your code with stbt_rig.py snapshot.
  6. Check the behaviour in the Object Repository on the Stb-tester portal.
  7. Repeat steps 1-6 until it’s working as desired.
  8. Make a git commit and open a pull request to share your changes with your team!

Save a screenshot

The first step is to save a screenshot of the relevant screen into selftest/screenshots/ in your test-pack. Give the file a descriptive name, not just “screenshot.png”. You can create subdirectories to organise your screenshots as you like.

By saving a screenshot, you will be able to develop and test your Page Object without actually running a test. This is much more efficient because running a test is relatively slow. These screenshots will be visible in the Object Repository on the Stb-tester Portal.

Remember to run git add to tell git to track the new screenshot.

Create a class that derives from stbt.FrameObject

For example:

class Menu(stbt.FrameObject):
    """Write some documentation about your class here.

    You might want to mention implementation details that others
    should be aware of if they're modifying this class, or
    details about the relevant business requirements, etc.
    """

Define a property called is_visible

images/roku-home-thumbnail.jpg

is_visible should return True if the particular page or screen is present.

For example, to identify the Roku’s main menu (shown here) we could look for the Roku logo roku-logo.png in the top corner and the white selection menu-selection.png in the left half of the screen.

class Menu(stbt.FrameObject):
    """The Roku's main menu."""

    @property
    def is_visible(self):
        ignore_text = stbt.MatchParameters(confirm_method="none")
        logo = stbt.match("images/roku-logo.png",
                          frame=self._frame)
        selection = stbt.match("images/menu-selection.png",
                               frame=self._frame,
                               match_parameters=ignore_text)
        return logo and selection

Note that every image-processing operation —such as stbt.match() or stbt.ocr()— must specify frame=self._frame. Otherwise the result will depend on the current live video; it won’t correspond to the FrameObject instance. Stb-tester’s static analysis will warn you if you’ve forgotten to specify frame inside a FrameObject property.

Inside a FrameObject property, every image-processing operation — such as stbt.match() or stbt.ocr()— must specify frame=self._frame

Define properties to extract information from the frame

Using the same Roku menu as an example, here’s a property that reads the text of the menu selection (“Home”, “My Feed”, “Settings”, etc):

class Menu(stbt.FrameObject):
    """The Roku's main menu."""

    @property
    def is_visible(self):
        ...  # implementation not shown

    @property
    def selection(self):
        return stbt.ocr(
            frame=self._frame,
            mode=stbt.OcrMode.SINGLE_LINE,
            # Fixed focus menu: The selection is always in the same position
            region=stbt.Region(x=116, y=160, right=477, bottom=205))

Once again: It’s important that we specify frame=self._frame when we call stbt.ocr().

Take a git snapshot of your code

Run ./stbt_rig.py snapshot to push a snapshot of your code to the Stb-tester portal. This will only push files known to git, so if you’ve made your FrameObject in a new file, use git add tests/myfile.py first so that git knows about your file.

./stbt_rig.py is a development tool that shortens the edit/test cycle when you’re working on your FrameObjects. It pushes a snapshot of your code to a git branch called “your_username/snapshot”. To install stbt_rig.py see Development Workflow.

If your IDE allows it, you can bind ./stbt_rig.py snapshot to your IDE’s “Save” button.

View your Page Object in the Stb-tester Portal

images/object-repository.png

Select your branch (your_username/snapshot), and select “Watch for changes and generate documentation”. Now, whenever you push changes with stbt_rig.py snapshot, the portal will show the screenshots you’ve added, do they match (according to the is_visible property that you defined), and the values of the other properties.