Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 1 | .. _tempest_test_writing: |
| 2 | |
| 3 | Tempest Test Writing Guide |
Matthew Treinish | 8f28d1f | 2017-04-08 21:35:41 -0400 | [diff] [blame] | 4 | ########################## |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 5 | |
| 6 | This guide serves as a starting point for developers working on writing new |
| 7 | Tempest tests. At a high level tests in Tempest are just tests that conform to |
| 8 | the standard python `unit test`_ framework. But there are several aspects of |
Jordan Pittier | 74a56ab | 2017-04-26 16:46:20 +0200 | [diff] [blame] | 9 | that are unique to Tempest and its role as an integration test suite running |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 10 | against a real cloud. |
| 11 | |
| 12 | .. _unit test: https://docs.python.org/3.6/library/unittest.html |
| 13 | |
Jordan Pittier | 74a56ab | 2017-04-26 16:46:20 +0200 | [diff] [blame] | 14 | .. note:: This guide is for writing tests in the Tempest repository. While many |
| 15 | parts of this guide are also applicable to Tempest plugins, not all |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 16 | the APIs mentioned are considered stable or recommended for use in |
| 17 | plugins. Please refer to :ref:`tempest_plugin` for details about |
| 18 | writing plugins |
| 19 | |
| 20 | |
| 21 | Adding a New TestCase |
| 22 | ===================== |
| 23 | |
| 24 | The base unit of testing in Tempest is the `TestCase`_ (also called the test |
| 25 | class). Each TestCase contains test methods which are the individual tests that |
| 26 | will be executed by the test runner. But, the TestCase is the smallest self |
Jordan Pittier | 74a56ab | 2017-04-26 16:46:20 +0200 | [diff] [blame] | 27 | contained unit for tests from the Tempest perspective. It's also the level at |
| 28 | which Tempest is parallel safe. In other words, multiple TestCases can be |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 29 | executed in parallel, but individual test methods in the same TestCase can not. |
| 30 | Also, all test methods within a TestCase are assumed to be executed serially. As |
| 31 | such you can use the test case to store variables that are shared between |
| 32 | methods. |
| 33 | |
| 34 | .. _TestCase: https://docs.python.org/3.6/library/unittest.html#unittest.TestCase |
| 35 | |
| 36 | In standard unittest the lifecycle of a TestCase can be described in the |
| 37 | following phases: |
| 38 | |
Masayuki Igawa | b78b923 | 2017-11-17 16:12:37 +0900 | [diff] [blame] | 39 | #. setUpClass |
| 40 | #. setUp |
| 41 | #. Test Execution |
| 42 | #. tearDown |
| 43 | #. doCleanups |
| 44 | #. tearDownClass |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 45 | |
| 46 | setUpClass |
| 47 | ---------- |
| 48 | |
| 49 | The setUpClass phase is the first phase executed by the test runner and is used |
| 50 | to perform any setup required for all the test methods to be executed. In |
| 51 | Tempest this is a very important step and will automatically do the necessary |
| 52 | setup for interacting with the configured cloud. |
| 53 | |
| 54 | To accomplish this you do **not** define a setUpClass function, instead there |
| 55 | are a number of predefined phases to setUpClass that are used. The phases are: |
| 56 | |
Masayuki Igawa | b78b923 | 2017-11-17 16:12:37 +0900 | [diff] [blame] | 57 | * skip_checks |
| 58 | * setup_credentials |
| 59 | * setup_clients |
| 60 | * resource_setup |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 61 | |
Andrea Frittoli | 3be5748 | 2017-08-25 22:41:26 +0100 | [diff] [blame] | 62 | which is executed in that order. Cleanup of resources provisioned during |
| 63 | the resource_setup must be scheduled right after provisioning using |
| 64 | the addClassResourceCleanp helper. The resource cleanups stacked this way |
| 65 | are executed in reverse order during tearDownClass, before the cleanup of |
| 66 | test credentials takes place. An example of a TestCase which defines all |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 67 | of these would be:: |
Masayuki Igawa | b78b923 | 2017-11-17 16:12:37 +0900 | [diff] [blame] | 68 | |
Andrea Frittoli | 3be5748 | 2017-08-25 22:41:26 +0100 | [diff] [blame] | 69 | from tempest.common import waiters |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 70 | from tempest import config |
Andrea Frittoli | 3be5748 | 2017-08-25 22:41:26 +0100 | [diff] [blame] | 71 | from tempest.lib.common.utils import test_utils |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 72 | from tempest import test |
| 73 | |
| 74 | CONF = config.CONF |
| 75 | |
| 76 | |
| 77 | class TestExampleCase(test.BaseTestCase): |
| 78 | |
| 79 | @classmethod |
| 80 | def skip_checks(cls): |
| 81 | """This section is used to evaluate config early and skip all test |
| 82 | methods based on these checks |
| 83 | """ |
| 84 | super(TestExampleCase, cls).skip_checks() |
| 85 | if not CONF.section.foo |
| 86 | cls.skip('A helpful message') |
| 87 | |
| 88 | @classmethod |
| 89 | def setup_credentials(cls): |
| 90 | """This section is used to do any manual credential allocation and also |
| 91 | in the case of dynamic credentials to override the default network |
| 92 | resource creation/auto allocation |
| 93 | """ |
| 94 | # This call is used to tell the credential allocator to not create any |
| 95 | # network resources for this test case. It also enables selective |
| 96 | # creation of other neutron resources. NOTE: it must go before the |
| 97 | # super call |
| 98 | cls.set_network_resources() |
| 99 | super(TestExampleCase, cls).setup_credentials() |
| 100 | |
| 101 | @classmethod |
| 102 | def setup_clients(cls): |
| 103 | """This section is used to setup client aliases from the manager object |
| 104 | or to initialize any additional clients. Except in a few very |
| 105 | specific situations you should not need to use this. |
| 106 | """ |
| 107 | super(TestExampleCase, cls).setup_clients() |
Jordan Pittier | 8160d31 | 2017-04-18 11:52:23 +0200 | [diff] [blame] | 108 | cls.servers_client = cls.os_primary.servers_client |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 109 | |
| 110 | @classmethod |
| 111 | def resource_setup(cls): |
| 112 | """This section is used to create any resources or objects which are |
| 113 | going to be used and shared by **all** test methods in the |
| 114 | TestCase. Note then anything created in this section must also be |
| 115 | destroyed in the corresponding resource_cleanup() method (which will |
| 116 | be run during tearDownClass()) |
| 117 | """ |
| 118 | super(TestExampleCase, cls).resource_setup() |
| 119 | cls.shared_server = cls.servers_client.create_server(...) |
Andrea Frittoli | 3be5748 | 2017-08-25 22:41:26 +0100 | [diff] [blame] | 120 | cls.addClassResourceCleanup(waiters.wait_for_server_termination, |
| 121 | cls.servers_client, |
| 122 | cls.shared_server['id']) |
| 123 | cls.addClassResourceCleanup( |
| 124 | test_utils.call_and_ignore_notfound_exc( |
| 125 | cls.servers_client.delete_server, |
| 126 | cls.shared_server['id'])) |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 127 | |
Matthew Treinish | 19b7ba4 | 2017-04-09 20:39:49 -0400 | [diff] [blame] | 128 | .. _credentials: |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 129 | |
| 130 | Allocating Credentials |
| 131 | '''''''''''''''''''''' |
| 132 | |
| 133 | Since Tempest tests are all about testing a running cloud, every test will need |
| 134 | credentials to be able to make API requests against the cloud. Since this is |
| 135 | critical to operation and, when running in parallel, easy to make a mistake, |
| 136 | the base TestCase class will automatically allocate a regular user for each |
| 137 | TestCase during the setup_credentials() phase. During this process it will also |
| 138 | initialize a client manager object using those credentials, which will be your |
| 139 | entry point into interacting with the cloud. For more details on how credentials |
| 140 | are allocated the :ref:`tempest_cred_provider_conf` section of the Tempest |
| 141 | Configuration Guide provides more details on the operation of this. |
| 142 | |
| 143 | There are some cases when you need more than a single set of credentials, or |
| 144 | credentials with a more specialized set of roles. To accomplish this you have |
| 145 | to set a class variable ``credentials`` on the TestCase directly. For example:: |
| 146 | |
| 147 | from tempest import test |
| 148 | |
| 149 | class TestExampleAdmin(test.BaseTestCase): |
| 150 | |
| 151 | credentials = ['primary', 'admin'] |
| 152 | |
| 153 | @classmethod |
| 154 | def skip_checks(cls): |
| 155 | ... |
| 156 | |
| 157 | In this example the ``TestExampleAdmin`` TestCase will allocate 2 sets of |
| 158 | credentials, one regular user and one admin user. The corresponding manager |
Jordan Pittier | 8160d31 | 2017-04-18 11:52:23 +0200 | [diff] [blame] | 159 | objects will be set as class variables ``cls.os_primary`` and ``cls.os_admin`` |
| 160 | respectively. You can also allocate a second user by putting **'alt'** in the |
| 161 | list too. A set of alt credentials are the same as primary but can be used |
| 162 | for tests cases that need a second user/project. |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 163 | |
| 164 | You can also specify credentials with specific roles assigned. This is useful |
| 165 | for cases where there are specific RBAC requirements hard coded into an API. |
| 166 | The canonical example of this are swift tests which often want to test swift's |
Jordan Pittier | 74a56ab | 2017-04-26 16:46:20 +0200 | [diff] [blame] | 167 | concepts of operator and reseller_admin. An actual example from Tempest on how |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 168 | to do this is:: |
| 169 | |
| 170 | class PublicObjectTest(base.BaseObjectTest): |
| 171 | |
| 172 | credentials = [['operator', CONF.object_storage.operator_role], |
| 173 | ['operator_alt', CONF.object_storage.operator_role]] |
| 174 | |
| 175 | @classmethod |
| 176 | def setup_credentials(cls): |
| 177 | super(PublicObjectTest, cls).setup_credentials() |
| 178 | ... |
| 179 | |
| 180 | In this case the manager objects will be set to ``cls.os_roles_operator`` and |
| 181 | ``cls.os_roles_operator_alt`` respectively. |
| 182 | |
| 183 | |
| 184 | There is no limit to how many credentials you can allocate in this manner, |
| 185 | however in almost every case you should **not** need more than 3 sets of |
| 186 | credentials per test case. |
| 187 | |
| 188 | To figure out the mapping of manager objects set on the TestCase and the |
| 189 | requested credentials you can reference: |
| 190 | |
| 191 | +-------------------+---------------------+ |
| 192 | | Credentials Entry | Manager Variable | |
| 193 | +===================+=====================+ |
zhufl | 0419088 | 2017-05-23 10:21:48 +0800 | [diff] [blame] | 194 | | primary | cls.os_primary | |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 195 | +-------------------+---------------------+ |
zhufl | 0419088 | 2017-05-23 10:21:48 +0800 | [diff] [blame] | 196 | | admin | cls.os_admin | |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 197 | +-------------------+---------------------+ |
| 198 | | alt | cls.os_alt | |
| 199 | +-------------------+---------------------+ |
| 200 | | [$label, $role] | cls.os_roles_$label | |
| 201 | +-------------------+---------------------+ |
| 202 | |
Jordan Pittier | 74a56ab | 2017-04-26 16:46:20 +0200 | [diff] [blame] | 203 | By default cls.os_primary is available since it is allocated in the base Tempest test |
zhufl | 2e644e6 | 2017-04-21 14:14:54 +0800 | [diff] [blame] | 204 | class (located in tempest/test.py). If your TestCase inherits from a different |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 205 | direct parent class (it'll still inherit from the BaseTestCase, just not |
| 206 | directly) be sure to check if that class overrides allocated credentials. |
| 207 | |
| 208 | Dealing with Network Allocation |
| 209 | ''''''''''''''''''''''''''''''' |
| 210 | |
Jordan Pittier | 74a56ab | 2017-04-26 16:46:20 +0200 | [diff] [blame] | 211 | When Neutron is enabled and a testing requires networking this isn't normally |
| 212 | automatically setup when a tenant is created. Since Tempest needs isolated |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 213 | tenants to function properly it also needs to handle network allocation. By |
| 214 | default the base test class will allocate a network, subnet, and router |
zhufl | 2e644e6 | 2017-04-21 14:14:54 +0800 | [diff] [blame] | 215 | automatically (this depends on the configured credential provider, for more |
| 216 | details see: :ref:`tempest_conf_network_allocation`). However, there are |
| 217 | situations where you do no need all of these resources allocated (or your |
| 218 | TestCase inherits from a class that overrides the default in tempest/test.py). |
Matthew Treinish | e8ab5f9 | 2017-03-01 15:25:39 -0500 | [diff] [blame] | 219 | There is a class level mechanism to override this allocation and specify which |
| 220 | resources you need. To do this you need to call `cls.set_network_resources()` |
| 221 | in the `setup_credentials()` method before the `super()`. For example:: |
| 222 | |
| 223 | from tempest import test |
| 224 | |
| 225 | |
| 226 | class TestExampleCase(test.BaseTestCase): |
| 227 | |
| 228 | @classmethod |
| 229 | def setup_credentials(cls): |
| 230 | cls.set_network_resources(network=True, subnet=True, router=False) |
| 231 | super(TestExampleCase, cls).setup_credentials() |
| 232 | |
| 233 | There are 2 quirks with the usage here. First for the set_network_resources |
| 234 | function to work properly it **must be called before super()**. This is so |
| 235 | that children classes' settings are always used instead of a parent classes'. |
| 236 | The other quirk here is that if you do not want to allocate any network |
| 237 | resources for your test class simply call `set_network_resources()` without |
| 238 | any arguments. For example:: |
| 239 | |
| 240 | from tempest import test |
| 241 | |
| 242 | |
| 243 | class TestExampleCase(test.BaseTestCase): |
| 244 | |
| 245 | @classmethod |
| 246 | def setup_credentials(cls): |
| 247 | cls.set_network_resources() |
| 248 | super(TestExampleCase, cls).setup_credentials() |
| 249 | |
| 250 | This will not allocate any networking resources. This is because by default all |
| 251 | the arguments default to False. |
| 252 | |
| 253 | It's also worth pointing out that it is common for base test classes for |
| 254 | different services (and scenario tests) to override this setting. When |
| 255 | inheriting from classes other than the base TestCase in tempest/test.py it is |
| 256 | worth checking the immediate parent for what is set to determine if your |
| 257 | class needs to override that setting. |
Matthew Treinish | 19b7ba4 | 2017-04-09 20:39:49 -0400 | [diff] [blame] | 258 | |
| 259 | Interacting with Credentials and Clients |
| 260 | ======================================== |
| 261 | |
| 262 | Once you have your basic TestCase setup you'll want to start writing tests. To |
| 263 | do that you need to interact with an OpenStack deployment. This section will |
| 264 | cover how credentials and clients are used inside of Tempest tests. |
| 265 | |
| 266 | |
| 267 | Manager Objects |
| 268 | --------------- |
| 269 | |
| 270 | The primary interface with which you interact with both credentials and |
| 271 | API clients is the client manager object. These objects are created |
zhufl | 2e644e6 | 2017-04-21 14:14:54 +0800 | [diff] [blame] | 272 | automatically by the base test class as part of credential setup (for more |
| 273 | details see the previous :ref:`credentials` section). Each manager object is |
Matthew Treinish | 19b7ba4 | 2017-04-09 20:39:49 -0400 | [diff] [blame] | 274 | initialized with a set of credentials and has each client object already setup |
| 275 | to use that set of credentials for making all the API requests. Each client is |
| 276 | accessible as a top level attribute on the manager object. So to start making |
| 277 | API requests you just access the client's method for making that call and the |
| 278 | credentials are already setup for you. For example if you wanted to make an API |
| 279 | call to create a server in Nova:: |
| 280 | |
| 281 | from tempest import test |
| 282 | |
| 283 | |
| 284 | class TestExampleCase(test.BaseTestCase): |
| 285 | def test_example_create_server(self): |
zhufl | 0419088 | 2017-05-23 10:21:48 +0800 | [diff] [blame] | 286 | self.os_primary.servers_client.create_server(...) |
Matthew Treinish | 19b7ba4 | 2017-04-09 20:39:49 -0400 | [diff] [blame] | 287 | |
zhufl | 0419088 | 2017-05-23 10:21:48 +0800 | [diff] [blame] | 288 | is all you need to do. As described previously, in the above example the |
| 289 | ``self.os_primary`` is created automatically because the base test class sets the |
| 290 | ``credentials`` attribute to allocate a primary credential set and initializes |
| 291 | the client manager as ``self.os_primary``. This same access pattern can be used |
| 292 | for all of the clients in Tempest. |
Matthew Treinish | 19b7ba4 | 2017-04-09 20:39:49 -0400 | [diff] [blame] | 293 | |
| 294 | Credentials Objects |
| 295 | ------------------- |
| 296 | |
zhufl | 2e644e6 | 2017-04-21 14:14:54 +0800 | [diff] [blame] | 297 | In certain cases you need direct access to the credentials (the most common |
Matthew Treinish | 19b7ba4 | 2017-04-09 20:39:49 -0400 | [diff] [blame] | 298 | use case would be an API request that takes a user or project id in the request |
zhufl | 2e644e6 | 2017-04-21 14:14:54 +0800 | [diff] [blame] | 299 | body). If you're in a situation where you need to access this you'll need to |
Matthew Treinish | 19b7ba4 | 2017-04-09 20:39:49 -0400 | [diff] [blame] | 300 | access the ``credentials`` object which is allocated from the configured |
| 301 | credential provider in the base test class. This is accessible from the manager |
| 302 | object via the manager's ``credentials`` attribute. For example:: |
| 303 | |
| 304 | from tempest import test |
| 305 | |
| 306 | |
| 307 | class TestExampleCase(test.BaseTestCase): |
| 308 | def test_example_create_server(self): |
zhufl | 0419088 | 2017-05-23 10:21:48 +0800 | [diff] [blame] | 309 | credentials = self.os_primary.credentials |
Matthew Treinish | 19b7ba4 | 2017-04-09 20:39:49 -0400 | [diff] [blame] | 310 | |
| 311 | The credentials object provides access to all of the credential information you |
| 312 | would need to make API requests. For example, building off the previous |
| 313 | example:: |
| 314 | |
| 315 | from tempest import test |
| 316 | |
| 317 | |
| 318 | class TestExampleCase(test.BaseTestCase): |
| 319 | def test_example_create_server(self): |
zhufl | 0419088 | 2017-05-23 10:21:48 +0800 | [diff] [blame] | 320 | credentials = self.os_primary.credentials |
Matthew Treinish | 19b7ba4 | 2017-04-09 20:39:49 -0400 | [diff] [blame] | 321 | username = credentials.username |
| 322 | user_id = credentials.user_id |
| 323 | password = credentials.password |
| 324 | tenant_id = credentials.tenant_id |