1
0

writing_tests.rst 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. .. _tutorial-salt-testing:
  2. ==================================
  3. Salt's Test Suite: An Introduction
  4. ==================================
  5. .. note::
  6. This tutorial makes a couple of assumptions. The first assumption is that
  7. you have a basic knowledge of Salt. To get up to speed, check out the
  8. :ref:`Salt Walkthrough <tutorial-salt-walk-through>`.
  9. The second assumption is that your Salt development environment is already
  10. configured and that you have a basic understanding of contributing to the
  11. Salt codebase. If you're unfamiliar with either of these topics, please refer
  12. to the :ref:`Installing Salt for Development<installing-for-development>`
  13. and the :ref:`Contributing<contributing>` pages, respectively.
  14. Salt comes with a powerful integration and unit test suite. The test suite
  15. allows for the fully automated run of integration and/or unit tests from a
  16. single interface.
  17. Salt's test suite is located under the ``tests`` directory in the root of Salt's
  18. code base and is divided into two main types of tests:
  19. :ref:`unit tests and integration tests <integration-vs-unit>`. The ``unit`` and
  20. ``integration`` sub-test-suites are located in the ``tests`` directory, which is
  21. where the majority of Salt's test cases are housed.
  22. .. _getting_set_up_for_tests:
  23. Getting Set Up For Tests
  24. ========================
  25. There are a couple of requirements, in addition to Salt's requirements, that need
  26. to be installed in order to run Salt's test suite. You can install these additional
  27. requirements using the files located in the ``salt/requirements`` directory,
  28. depending on your relevant version of Python:
  29. .. code-block:: bash
  30. pip install -r requirements/dev_python27.txt
  31. pip install -r requirements/dev_python34.txt
  32. To be able to run integration tests which utilizes ZeroMQ transport, you also
  33. need to install additional requirements for it. Make sure you have installed
  34. the C/C++ compiler and development libraries and header files needed for your
  35. Python version.
  36. This is an example for RedHat-based operating systems:
  37. .. code-block:: bash
  38. yum install gcc gcc-c++ python-devel
  39. pip install -r requirements/zeromq.txt
  40. On Debian, Ubuntu or their derivatives run the following commands:
  41. .. code-block:: bash
  42. apt-get install build-essential python-dev
  43. pip install -r requirements/zeromq.txt
  44. This will install the latest ``pycrypto`` and ``pyzmq`` (with bundled
  45. ``libzmq``) Python modules required for running integration tests suite.
  46. Test Directory Structure
  47. ========================
  48. As noted in the introduction to this tutorial, Salt's test suite is located in the
  49. ``tests`` directory in the root of Salt's code base. From there, the tests are divided
  50. into two groups ``integration`` and ``unit``. Within each of these directories, the
  51. directory structure roughly mirrors the directory structure of Salt's own codebase.
  52. For example, the files inside ``tests/integration/modules`` contains tests for the
  53. files located within ``salt/modules``.
  54. .. note::
  55. ``tests/integration`` and ``tests/unit`` are the only directories discussed in
  56. this tutorial. With the exception of the ``tests/runtests.py`` file, which is
  57. used below in the `Running the Test Suite`_ section, the other directories and
  58. files located in ``tests`` are outside the scope of this tutorial.
  59. .. _integration-vs-unit:
  60. Integration vs. Unit
  61. --------------------
  62. Given that Salt's test suite contains two powerful, though very different, testing
  63. approaches, when should you write integration tests and when should you write unit
  64. tests?
  65. Integration tests use Salt masters, minions, and a syndic to test salt functionality
  66. directly and focus on testing the interaction of these components. Salt's integration
  67. test runner includes functionality to run Salt execution modules, runners, states,
  68. shell commands, salt-ssh commands, salt-api commands, and more. This provides a
  69. tremendous ability to use Salt to test itself and makes writing such tests a breeze.
  70. Integration tests are the preferred method of testing Salt functionality when
  71. possible.
  72. Unit tests do not spin up any Salt daemons, but instead find their value in testing
  73. singular implementations of individual functions. Instead of testing against specific
  74. interactions, unit tests should be used to test a function's logic. Unit tests should
  75. be used to test a function's exit point(s) such as any ``return`` or ``raises``
  76. statements.
  77. Unit tests are also useful in cases where writing an integration test might not be
  78. possible. While the integration test suite is extremely powerful, unfortunately at
  79. this time, it does not cover all functional areas of Salt's ecosystem. For example,
  80. at the time of this writing, there is not a way to write integration tests for Proxy
  81. Minions. Since the test runner will need to be adjusted to account for Proxy Minion
  82. processes, unit tests can still provide some testing support in the interim by
  83. testing the logic contained inside Proxy Minion functions.
  84. Running the Test Suite
  85. ======================
  86. Once all of the :ref:`requirements <getting_set_up_for_tests>` are installed, the
  87. ``runtests.py`` file in the ``salt/tests`` directory is used to instantiate
  88. Salt's test suite:
  89. .. code-block:: bash
  90. python tests/runtests.py [OPTIONS]
  91. The command above, if executed without any options, will run the entire suite of
  92. integration and unit tests. Some tests require certain flags to run, such as
  93. destructive tests. If these flags are not included, then the test suite will only
  94. perform the tests that don't require special attention.
  95. At the end of the test run, you will see a summary output of the tests that passed,
  96. failed, or were skipped.
  97. The test runner also includes a ``--help`` option that lists all of the various
  98. command line options:
  99. .. code-block:: bash
  100. python tests/runtests.py --help
  101. You can also call the test runner as an executable:
  102. .. code-block:: bash
  103. ./tests/runtests.py --help
  104. Running Integration Tests
  105. -------------------------
  106. Salt's set of integration tests use Salt to test itself. The integration portion
  107. of the test suite includes some built-in Salt daemons that will spin up in preparation
  108. of the test run. This list of Salt daemon processes includes:
  109. * 2 Salt Masters
  110. * 2 Salt Minions
  111. * 1 Salt Syndic
  112. These various daemons are used to execute Salt commands and functionality within
  113. the test suite, allowing you to write tests to assert against expected or
  114. unexpected behaviors.
  115. A simple example of a test utilizing a typical master/minion execution module command
  116. is the test for the ``test_ping`` function in the
  117. ``tests/integration/modules/test_test.py``
  118. file:
  119. .. code-block:: python
  120. def test_ping(self):
  121. '''
  122. test.ping
  123. '''
  124. self.assertTrue(self.run_function('test.ping'))
  125. The test above is a very simple example where the ``test.ping`` function is
  126. executed by Salt's test suite runner and is asserting that the minion returned
  127. with a ``True`` response.
  128. .. _test-selection-options:
  129. Test Selection Options
  130. ~~~~~~~~~~~~~~~~~~~~~~
  131. If you look in the output of the ``--help`` command of the test runner, you will
  132. see a section called ``Tests Selection Options``. The options under this section
  133. contain various subsections of the integration test suite such as ``--modules``,
  134. ``--ssh``, or ``--states``. By selecting any one of these options, the test daemons
  135. will spin up and the integration tests in the named subsection will run.
  136. .. code-block:: bash
  137. ./tests/runtests.py --modules
  138. .. note::
  139. The testing subsections listed in the ``Tests Selection Options`` of the
  140. ``--help`` output *only* apply to the integration tests. They do not run unit
  141. tests.
  142. Running Unit Tests
  143. ------------------
  144. While ``./tests/runtests.py`` executes the *entire* test suite (barring any tests
  145. requiring special flags), the ``--unit`` flag can be used to run *only* Salt's
  146. unit tests. Salt's unit tests include the tests located in the ``tests/unit``
  147. directory.
  148. The unit tests do not spin up any Salt testing daemons as the integration tests
  149. do and execute very quickly compared to the integration tests.
  150. .. code-block:: bash
  151. ./tests/runtests.py --unit
  152. .. _running-specific-tests:
  153. Running Specific Tests
  154. ----------------------
  155. There are times when a specific test file, test class, or even a single,
  156. individual test need to be executed, such as when writing new tests. In these
  157. situations, the ``--name`` option should be used.
  158. For running a single test file, such as the pillar module test file in the
  159. integration test directory, you must provide the file path using ``.`` instead
  160. of ``/`` as separators and no file extension:
  161. .. code-block:: bash
  162. ./tests/runtests.py --name=integration.modules.test_pillar
  163. ./tests/runtests.py -n integration.modules.test_pillar
  164. Some test files contain only one test class while other test files contain multiple
  165. test classes. To run a specific test class within the file, append the name of
  166. the test class to the end of the file path:
  167. .. code-block:: bash
  168. ./tests/runtests.py --name=integration.modules.test_pillar.PillarModuleTest
  169. ./tests/runtests.py -n integration.modules.test_pillar.PillarModuleTest
  170. To run a single test within a file, append both the name of the test class the
  171. individual test belongs to, as well as the name of the test itself:
  172. .. code-block:: bash
  173. ./tests/runtests.py \
  174. --name=integration.modules.test_pillar.PillarModuleTest.test_data
  175. ./tests/runtests.py \
  176. -n integration.modules.test_pillar.PillarModuleTest.test_data
  177. The ``--name`` and ``-n`` options can be used for unit tests as well as integration
  178. tests. The following command is an example of how to execute a single test found in
  179. the ``tests/unit/modules/test_cp.py`` file:
  180. .. code-block:: bash
  181. ./tests/runtests.py \
  182. -n unit.modules.test_cp.CpTestCase.test_get_template_success
  183. Writing Tests for Salt
  184. ======================
  185. Once you're comfortable running tests, you can now start writing them! Be sure
  186. to review the `Integration vs. Unit`_ section of this tutorial to determine what
  187. type of test makes the most sense for the code you're testing.
  188. .. note::
  189. There are many decorators, naming conventions, and code specifications
  190. required for Salt test files. We will not be covering all of the these specifics
  191. in this tutorial. Please refer to the testing documentation links listed below
  192. in the `Additional Testing Documentation`_ section to learn more about these
  193. requirements.
  194. In the following sections, the test examples assume the "new" test is added to
  195. a test file that is already present and regularly running in the test suite and
  196. is written with the correct requirements.
  197. Writing Integration Tests
  198. -------------------------
  199. Since integration tests validate against a running environment, as explained in the
  200. `Running Integration Tests`_ section of this tutorial, integration tests are very
  201. easy to write and are generally the preferred method of writing Salt tests.
  202. The following integration test is an example taken from the ``test.py`` file in the
  203. ``tests/integration/modules`` directory. This test uses the ``run_function`` method
  204. to test the functionality of a traditional execution module command.
  205. The ``run_function`` method uses the integration test daemons to execute a
  206. ``module.function`` command as you would with Salt. The minion runs the function and
  207. returns. The test also uses `Python's Assert Functions`_ to test that the
  208. minion's return is expected.
  209. .. code-block:: python
  210. def test_ping(self):
  211. '''
  212. test.ping
  213. '''
  214. self.assertTrue(self.run_function('test.ping'))
  215. Args can be passed in to the ``run_function`` method as well:
  216. .. code-block:: python
  217. def test_echo(self):
  218. '''
  219. test.echo
  220. '''
  221. self.assertEqual(self.run_function('test.echo', ['text']), 'text')
  222. The next example is taken from the
  223. ``tests/integration/modules/test_aliases.py`` file and
  224. demonstrates how to pass kwargs to the ``run_function`` call. Also note that this
  225. test uses another salt function to ensure the correct data is present (via the
  226. ``aliases.set_target`` call) before attempting to assert what the ``aliases.get_target``
  227. call should return.
  228. .. code-block:: python
  229. def test_set_target(self):
  230. '''
  231. aliases.set_target and aliases.get_target
  232. '''
  233. set_ret = self.run_function(
  234. 'aliases.set_target',
  235. alias='fred',
  236. target='bob')
  237. self.assertTrue(set_ret)
  238. tgt_ret = self.run_function(
  239. 'aliases.get_target',
  240. alias='fred')
  241. self.assertEqual(tgt_ret, 'bob')
  242. Using multiple Salt commands in this manner provides two useful benefits. The first is
  243. that it provides some additional coverage for the ``aliases.set_target`` function.
  244. The second benefit is the call to ``aliases.get_target`` is not dependent on the
  245. presence of any aliases set outside of this test. Tests should not be dependent on
  246. the previous execution, success, or failure of other tests. They should be isolated
  247. from other tests as much as possible.
  248. While it might be tempting to build out a test file where tests depend on one another
  249. before running, this should be avoided. SaltStack recommends that each test should
  250. test a single functionality and not rely on other tests. Therefore, when possible,
  251. individual tests should also be broken up into singular pieces. These are not
  252. hard-and-fast rules, but serve more as recommendations to keep the test suite simple.
  253. This helps with debugging code and related tests when failures occur and problems
  254. are exposed. There may be instances where large tests use many asserts to set up a
  255. use case that protects against potential regressions.
  256. .. note::
  257. The examples above all use the ``run_function`` option to test execution module
  258. functions in a traditional master/minion environment. To see examples of how to
  259. test other common Salt components such as runners, salt-api, and more, please
  260. refer to the :ref:`Integration Test Class Examples<integration-class-examples>`
  261. documentation.
  262. Destructive vs Non-destructive Tests
  263. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  264. Since Salt is used to change the settings and behavior of systems, often, the
  265. best approach to run tests is to make actual changes to an underlying system.
  266. This is where the concept of destructive integration tests comes into play.
  267. Tests can be written to alter the system they are running on. This capability
  268. is what fills in the gap needed to properly test aspects of system management
  269. like package installation.
  270. To write a destructive test, import and use the ``destructiveTest`` decorator for
  271. the test method:
  272. .. code-block:: python
  273. import integration
  274. from tests.support.helpers import destructiveTest
  275. class PkgTest(integration.ModuleCase):
  276. @destructiveTest
  277. def test_pkg_install(self):
  278. ret = self.run_function('pkg.install', name='finch')
  279. self.assertSaltTrueReturn(ret)
  280. ret = self.run_function('pkg.purge', name='finch')
  281. self.assertSaltTrueReturn(ret)
  282. Writing Unit Tests
  283. ------------------
  284. As explained in the `Integration vs. Unit`_ section above, unit tests should be
  285. written to test the *logic* of a function. This includes focusing on testing
  286. ``return`` and ``raises`` statements. Substantial effort should be made to mock
  287. external resources that are used in the code being tested.
  288. External resources that should be mocked include, but are not limited to, APIs,
  289. function calls, external data either globally available or passed in through
  290. function arguments, file data, etc. This practice helps to isolate unit tests to
  291. test Salt logic. One handy way to think about writing unit tests is to "block
  292. all of the exits". More information about how to properly mock external resources
  293. can be found in Salt's :ref:`Unit Test<unit-tests>` documentation.
  294. Salt's unit tests utilize Python's mock class as well as `MagicMock`_. The
  295. ``@patch`` decorator is also heavily used when "blocking all the exits".
  296. A simple example of a unit test currently in use in Salt is the
  297. ``test_get_file_not_found`` test in the ``tests/unit/modules/test_cp.py`` file.
  298. This test uses the ``@patch`` decorator and ``MagicMock`` to mock the return
  299. of the call to Salt's ``cp.hash_file`` execution module function. This ensures
  300. that we're testing the ``cp.get_file`` function directly, instead of inadvertently
  301. testing the call to ``cp.hash_file``, which is used in ``cp.get_file``.
  302. .. code-block:: python
  303. def test_get_file_not_found(self):
  304. '''
  305. Test if get_file can't find the file.
  306. '''
  307. with patch('salt.modules.cp.hash_file', MagicMock(return_value=False)):
  308. path = 'salt://saltines'
  309. dest = '/srv/salt/cheese'
  310. ret = ''
  311. self.assertEqual(cp.get_file(path, dest), ret)
  312. Note that Salt's ``cp`` module is imported at the top of the file, along with all
  313. of the other necessary testing imports. The ``get_file`` function is then called
  314. directed in the testing function, instead of using the ``run_function`` method as
  315. the integration test examples do above.
  316. The call to ``cp.get_file`` returns an empty string when a ``hash_file`` isn't found.
  317. Therefore, the example above is a good illustration of a unit test "blocking
  318. the exits" via the ``@patch`` decorator, as well as testing logic via asserting
  319. against the ``return`` statement in the ``if`` clause.
  320. There are more examples of writing unit tests of varying complexities available
  321. in the following docs:
  322. * :ref:`Simple Unit Test Example<simple-unit-example>`
  323. * :ref:`Complete Unit Test Example<complete-unit-example>`
  324. * :ref:`Complex Unit Test Example<complex-unit-example>`
  325. .. note::
  326. Considerable care should be made to ensure that you're testing something
  327. useful in your test functions. It is very easy to fall into a situation
  328. where you have mocked so much of the original function that the test
  329. results in only asserting against the data you have provided. This results
  330. in a poor and fragile unit test.
  331. Checking for Log Messages
  332. =========================
  333. To test to see if a given log message has been emitted, the following pattern
  334. can be used
  335. .. code-block:: python
  336. # Import logging handler
  337. from tests.support.helpers import TstSuiteLoggingHandler
  338. # .. inside test
  339. with TstSuiteLoggingHandler() as handler:
  340. for message in handler.messages:
  341. if message.startswith('ERROR: This is the error message we seek'):
  342. break
  343. else:
  344. raise AssertionError('Did not find error message')
  345. Automated Test Runs
  346. ===================
  347. SaltStack maintains a Jenkins server which can be viewed at
  348. https://jenkinsci.saltstack.com. The tests executed from this Jenkins server
  349. create fresh virtual machines for each test run, then execute the destructive
  350. tests on the new, clean virtual machine. This allows for the execution of tests
  351. across supported platforms.
  352. Additional Testing Documentation
  353. ================================
  354. In addition to this tutorial, there are some other helpful resources and documentation
  355. that go into more depth on Salt's test runner, writing tests for Salt code, and general
  356. Python testing documentation. Please see the follow references for more information:
  357. * :ref:`Salt's Test Suite Documentation<salt-test-suite>`
  358. * :ref:`Integration Tests<integration-tests>`
  359. * :ref:`Unit Tests<unit-tests>`
  360. * `MagicMock`_
  361. * `Python Unittest`_
  362. * `Python's Assert Functions`_
  363. .. _MagicMock: https://docs.python.org/3/library/unittest.mock.html
  364. .. _Python Unittest: https://docs.python.org/2/library/unittest.html
  365. .. _Python's Assert Functions: https://docs.python.org/2/library/unittest.html#assert-methods