qubes-tests.rst 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. :py:mod:`qubes.tests` -- Writing tests for qubes
  2. ================================================
  3. Writing tests is very important for ensuring quality of code that is delivered.
  4. Given test case may check for variety of conditions, but they generally fall
  5. inside those two categories of conformance tests:
  6. * Unit tests: these test smallest units of code, probably methods of functions,
  7. or even combination of arguments for one specific method.
  8. * Integration tests: these test interworking of units.
  9. We are interested in both categories.
  10. There is also distinguished category of regression tests (both unit- and
  11. integration-level), which are included because they check for specific bugs that
  12. were fixed in the past and should not happen in the future. Those should be
  13. accompanied with reference to closed ticked that describes the bug.
  14. Qubes' tests are written using :py:mod:`unittest` module from Python Standard
  15. Library for both unit test and integration tests.
  16. Test case organisation
  17. ----------------------
  18. Every module (like :py:mod:`qubes.vm.qubesvm`) should have its companion (like
  19. ``qubes.tests.vm.qubesvm``). Packages ``__init__.py`` files should be
  20. accompanied by ``init.py`` inside respective directory under :file:`tests/`.
  21. Inside tests module there should be one :py:class:`qubes.tests.QubesTestCase`
  22. class for each class in main module plus one class for functions and global
  23. variables. :py:class:`qubes.tests.QubesTestCase` classes should be named
  24. ``TC_xx_ClassName``, where ``xx`` is two-digit number. Test functions should be
  25. named ``test_xxx_test_name``, where ``xxx`` is three-digit number. You may
  26. introduce some structure of your choice in this number.
  27. FIXME: where are placed integration tests?
  28. Writing tests
  29. -------------
  30. First of all, testing is art, not science. Testing is not panaceum and won't
  31. solve all of your problems. Rules given in this guide and elsewhere should be
  32. followed, but shouldn't be worshipped.
  33. Test can be divided into three phases. The first part is setup phase. In this
  34. part you should arrange for a test condition to occur. You intentionally put
  35. system under test in some specific state. Phase two is executing test condition
  36. -- for example you check some variable for equality or expect that some
  37. exception is raised. Phase three is responsible for returning a verdict. This is
  38. largely done by the framework.
  39. When writing test, you should think about order of execution. This is the reason
  40. of numbers in names of the classes and test methods. Tests should be written
  41. bottom-to-top, that is, test setups that are ran later may depend on features
  42. that are tested after but not the other way around. This is important, because
  43. when encountering failure we expect the reason happen *before*, and not after
  44. failure occured. Therefore, when encountering multiple errors, we may instantly
  45. focus on fixing the first one and not wondering if any later problems may be
  46. relevant or not. Some people also like to enable
  47. :py:attr:`unittest.TestResult.failfast` feature, which stops on the first failed
  48. test -- with wrong order this messes up their workflow.
  49. Test should fail for one reason only and test one specific issue. This does not
  50. mean that you can use one ``.assert*`` method per ``test_`` function: for
  51. example when testing one regular expression you are welcome to test many valid
  52. and/or invalid inputs, especcialy when test setup is complicated. However, if
  53. you encounter problems during setup phase, you should *skip* the test, and not
  54. fail it. This also aids interpretation of results.
  55. You may, when it makes sense, manipulate private members of classes under tests.
  56. This violates one of the founding principles of object-oriented programming, but
  57. may be required to write tests in correct order if your class provides public
  58. methods with circular dependencies. For example containers may check if added
  59. item is already in container, but you can't test ``__contains__`` method without
  60. something already inside. Don't forget to test the other method later.
  61. Special Qubes-specific considerations
  62. -------------------------------------
  63. Events
  64. ^^^^^^
  65. :py:class:`qubes.tests.QubesTestCase` provides convenient methods for checking
  66. if event fired or not: :py:meth:`qubes.tests.QubesTestCase.assertEventFired` and
  67. :py:meth:`qubes.tests.QubesTestCase.assertEventNotFired`. These require that
  68. emitter is subclass of :py:class:`qubes.tests.TestEmitter`. You may instantiate
  69. it directly::
  70. import qubes.tests
  71. class TC_10_SomeClass(qubes.tests.QubesTestCase):
  72. def test_000_event(self):
  73. emitter = qubes.tests.TestEmitter()
  74. emitter.fire_event('did-fire')
  75. self.assertEventFired(emitter, 'did-fire')
  76. If you need to snoop specific class (which already is a child of
  77. :py:class:`qubes.events.Emitter`, possibly indirect), you can define derivative
  78. class which uses :py:class:`qubes.tests.TestEmitter` as mix-in::
  79. import qubes
  80. import qubes.tests
  81. class TestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
  82. pass
  83. class TC_20_PropertyHolder(qubes.tests.QubesTestCase):
  84. def test_000_event(self):
  85. emitter = TestHolder()
  86. self.assertEventNotFired(emitter, 'did-not-fire')
  87. Dom0
  88. ^^^^
  89. Qubes is a complex piece of software and depends on number other complex pieces,
  90. notably VM hypervisor or some other isolation provider. Not everything may be
  91. testable under all conditions. Some tests (mainly unit tests) are expected to
  92. run during compilation, but many tests (probably all of the integration tests
  93. and more) can run only inside already deployed Qubes installation. There is
  94. special decorator, :py:func:`qubes.tests.skipUnlessDom0` which causes test (or
  95. even entire class) to be skipped outside dom0. Use it freely::
  96. import qubes.tests
  97. class TC_30_SomeClass(qubes.tests.QubesTestCase):
  98. @qubes.tests.skipUnlessDom0
  99. def test_000_inside_dom0(self):
  100. # this is skipped outside dom0
  101. pass
  102. @qubes.tests.skipUnlessDom0
  103. class TC_31_SomeOtherClass(qubes.tests.QubesTestCase):
  104. # all tests in this class are skipped
  105. pass
  106. Module contents
  107. ---------------
  108. .. automodule:: qubes.tests
  109. :members:
  110. :show-inheritance:
  111. .. vim: ts=3 sw=3 et tw=80