ert: Tests and Their Environment
3.3 Tests and Their Environment
===============================
Sometimes, it doesn’t make sense to run a test due to missing
preconditions. A required Emacs feature might not be compiled in, the
function to be tested could call an external binary which might not be
available on the test machine, you name it. In this case, the macro
‘skip-unless’ could be used to skip the test:
(ert-deftest test-dbus ()
"A test that checks D-BUS functionality."
(skip-unless (featurep 'dbusbind))
...)
The outcome of running a test should not depend on the current state
of the environment, and each test should leave its environment in the
same state it found it in. In particular, a test should not depend on
any Emacs customization variables or hooks, and if it has to make any
changes to Emacs’s state or state external to Emacs (such as the file
system), it should undo these changes before it returns, regardless of
whether it passed or failed.
Tests should not depend on the environment because any such
dependencies can make the test brittle or lead to failures that occur
only under certain circumstances and are hard to reproduce. Of course,
the code under test may have settings that affect its behavior. In that
case, it is best to make the test ‘let’-bind all such setting variables
to set up a specific configuration for the duration of the test. The
test can also set up a number of different configurations and run the
code under test with each.
Tests that have side effects on their environment should restore it
to its original state because any side effects that persist after the
test can disrupt the workflow of the programmer running the tests. If
the code under test has side effects on Emacs’s current state, such as
on the current buffer or window configuration, the test should create a
temporary buffer for the code to manipulate (using ‘with-temp-buffer’),
or save and restore the window configuration (using
‘save-window-excursion’), respectively. For aspects of the state that
can not be preserved with such macros, cleanup should be performed with
‘unwind-protect’, to ensure that the cleanup occurs even if the test
fails.
An exception to this are messages that the code under test prints
with ‘message’ and similar logging; tests should not bother restoring
the ‘*Message*’ buffer to its original state.
The above guidelines imply that tests should avoid calling highly
customizable commands such as ‘find-file’, except, of course, if such
commands are what they want to test. The exact behavior of ‘find-file’
depends on many settings such as ‘find-file-wildcards’,
‘enable-local-variables’, and ‘auto-mode-alist’. It is difficult to
write a meaningful test if its behavior can be affected by so many
external factors. Also, ‘find-file’ has side effects that are hard to
predict and thus hard to undo: It may create a new buffer or reuse an
existing buffer if one is already visiting the requested file; and it
runs ‘find-file-hook’, which can have arbitrary side effects.
Instead, it is better to use lower-level mechanisms with simple and
predictable semantics like ‘with-temp-buffer’, ‘insert’ or
‘insert-file-contents-literally’, and to activate any desired mode by
calling the corresponding function directly, after binding the hook
variables to ‘nil’. This avoids the above problems.