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