OpenStack Horizon: integration tests for plugins

Ideally, I need to push this post into the Horizon documentation, but I would like to publish it here as HOTO for me before everything will be approved and I move this into the official documentation.

About one week ago we, finally, included integration tests job to our gates in non-voting mode. A big thanks to everyone who helped to get this done. We’ve got about 80 integrations tests in the horizon repository. Also, it could be used as a base framework to implement tests for plugins. Unfortunately, these tests work only with python2 now, but we’re going to fix it soon.

This is a step-by-step guide on how to implement integration tests for your Horizon plugin. I’ll take vitrage-dashboard as an example.

Step #1: tox.ini

You have to add a tox environment to run new tests. I’m using the same, as we’ve got in Horizon:

[testenv:integration]
# Run integration tests only
passenv = AVCONV_INSTALLED
setenv =
  PYTHONHASHSEED=0
  INTEGRATION_TESTS=1
  SELENIUM_HEADLESS=1
  HORIZON_INTEGRATION_TESTS_CONFIG_FILE=vitrage_dashboard/dashboard/tests/integration/horizon.conf
basepython = python2.7
commands = {envpython} {toxinidir}/manage.py test vitrage_dashboard {posargs}

INTEGRATION_TESTS and SELENIUM_HEADLESS are needed to configure selenium on your host.

HORIZON_INTEGRATION_TESTS_CONFIG_FILE variable should point to your plugin-specific configuration file which will be described in the next section. Please, make sure that you’ve got python2 there :(.

Step #2: configuration file

We use oslo.config to work with configuration file. A minimal config file looks like:

[plugin]
is_plugin=True
plugin_page_path=vitrage_dashboard.dashboard.tests.integration.pages
plugin_page_structure='{"Project": {"Vitrage": {"_": ["Topology", "Alarms"]}}}'

All other settings will be used from horizon’s config. Since Horizon uses (Page Objects)[https://wiki.mozilla.org/QA/Execution/Web_Testing/Docs/Automation/StyleGuide#Page_Objects] pattern in the integration tests, it’s required to implement the same solution in plugins. That’s why we need to specify a path to Page Objects implementation.

Also, you need to specify your plugins page structure like we’ve got for core pages in plugin_page_structure param as a JSON string representation.

Step #3: Page Objects

There is a requirement for Horizon integration tests framework to have Page Object class for each page. As an any OOP approach, it work’s well for a lot of code(pages) but looks like a over-engineering for simple tests for few pages. The structure of these modules should be like:

pages/__init__.py
pages/project/__init__.py
pages/project/vitrage/__init__.py
pages/project/vitrage/alarmspage.py

It should be located in the module, specified in plugin_page_path.

Step #4: Implement tests

It’s up to you how you implement integration tests. Horizon provides some useful helpers for navigation, base TestCase and AdminTestCase for your tests.

The simplest test I implemented looks like:

class TestVitrageDashboardInstalled(helpers.TestCase):
    def test_alarms_page_opened(self):
        alarms_page = self.home_pg.go_to_project_vitrage_alarmspage()
        self.assertEqual(alarms_page.page_title,
                         'Alarms Analysis - OpenStack Dashboard')

It just opens the required page and verifies it’s title.

Step #5: test requirements

You have to add these two libraries to your test-requirements.txt to get everything working:

# integration tests requirements
selenium>=2.50.1 # Apache-2.0
xvfbwrapper>=0.1.3 #license: MIT

Step 6: Enjoy!

You can run your tests now:

tox -e integration

A sample code could be found here: https://review.openstack.org/634487

UPDATE: Part 2, CI setup is here.

Tags

.net .net-framework .net-framework-3.5 agile ajax ajax-control-toolkit ampq ansible apache asp.net asp.net-mvc automation axum babel bash benchmark blog blog-engine bootstrap buildout c# cache centos chrome ci cinder ckan cli cloud code-review codeplex community config debugger deface dependencies development-environment devices devstack devtime disks django dlr dns docker dockerimage dos easy_install elmah encoding environment-variables error event events everything-as-a-code exception exceptions fabrik firefox flask foreach forms fstab gae gcc gerrit git github go google google-app-engine grep hack hacked hardware headless horizon hound html hugo iaas ienumerable iis internet iptables iron-python ironic iscsi java-script javascript jenkins jquery js jsx kharkivpy kiss kombu kvm kyiv lettuce libvirt linux lio loci logging loopback losetup lvm mac-os macos mercurial microsoft microsoft-sync-framework mobile mono ms-office msbuild networking news nginx npm npx offtopic oop open-source open-xml opensource openstack openvswitch os packages paraller-development patterns-practices performance php pika pip plugins pnp podcast popup postgresql profiler project protocols proxy pycamp pycharm pycon pykyiv pylint pypi python python-3 qcow quantum qumy rabbitmq rar react reactjs refactoring rfc rhel search-engine security selenium server shell silverlight socket software-engineering source-control sourcegear-vault sources sql sql-server sql-server-express sqlalchemy ssh static-site sublimetext svg tests tgt tipfy tornado typescript uapycon ui uneta unit-tests upgrades usability vim virtualenv visual-studio vitrage vm vue.js vuejs web-development web-server web-service web_root webpack webroot windows windows-live word-press x32 x64 xcode xml xss xvfb интернет-магазин книги


Archives

2019 (73)
2018 (2)
2017 (3)
2016 (2)
2015 (3)
2014 (5)
2013 (17)
2012 (22)
2011 (35)
2010 (25)
2009 (35)
2008 (32)
2007 (2)