diff options
Diffstat (limited to 'doc/misc/ert.texi')
-rw-r--r-- | doc/misc/ert.texi | 841 |
1 files changed, 841 insertions, 0 deletions
diff --git a/doc/misc/ert.texi b/doc/misc/ert.texi new file mode 100644 index 0000000000..978cac6992 --- /dev/null +++ b/doc/misc/ert.texi @@ -0,0 +1,841 @@ +\input texinfo +@c %**start of header +@setfilename ../../info/ert +@settitle Emacs Lisp Regression Testing +@c %**end of header + +@dircategory Emacs +@direntry +* ERT: (ert). Emacs Lisp Regression Testing. +@end direntry + +@copying +Copyright @copyright{} 2008, 2010-2011 Free Software Foundation, Inc. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, with the Front-Cover texts being ``A GNU Manual,'' +and with the Back-Cover Texts as in (a) below. A copy of the license +is included in the section entitled ``GNU Free Documentation License'' +in the Emacs manual. + +(a) The FSF's Back-Cover Text is: ``You have the freedom to copy and +modify this GNU manual. Buying copies from the FSF supports it in +developing GNU and promoting software freedom.'' + +This document is part of a collection distributed under the GNU Free +Documentation License. If you want to distribute this document +separately from the collection, you can do so by adding a copy of the +license to the document, as described in section 6 of the license. +@end quotation +@end copying + +@node Top, Introduction, (dir), (dir) +@top ERT: Emacs Lisp Regression Testing + +ERT is a tool for automated testing in Emacs Lisp. Its main features +are facilities for defining tests, running them and reporting the +results, and for debugging test failures interactively. + +ERT is similar to tools for other environments such as JUnit, but has +unique features that take advantage of the dynamic and interactive +nature of Emacs. Despite its name, it works well both for test-driven +development (see +@url{http://en.wikipedia.org/wiki/Test-driven_development}) and for +traditional software development methods. + +@menu +* Introduction:: A simple example of an ERT test. +* How to Run Tests:: Run tests in your Emacs or from the command line. +* How to Write Tests:: How to add tests to your Emacs Lisp code. +* How to Debug Tests:: What to do if a test fails. +* Extending ERT:: ERT is extensible in several ways. +* Other Testing Concepts:: Features not in ERT. + +@detailmenu + --- The Detailed Node Listing --- + +How to Run Tests + +* Running Tests Interactively:: Run tests in your current Emacs. +* Running Tests in Batch Mode:: Run tests in emacs -Q. +* Test Selectors:: Choose which tests to run. + +How to Write Tests + +* The @code{should} Macro:: A powerful way to express assertions. +* Expected Failures:: Tests for known bugs. +* Tests and Their Environment:: Don't depend on customizations; no side effects. +* Useful Techniques:: Some examples. + +How to Debug Tests + +* Understanding Explanations:: How ERT gives details on why an assertion failed. +* Interactive Debugging:: Tools available in the ERT results buffer. + +Extending ERT + +* Defining Explanation Functions:: Teach ERT about more predicates. +* Low-Level Functions for Working with Tests:: Use ERT's data for your purposes. + +Other Testing Concepts + +* Mocks and Stubs:: Stubbing out code that is irrelevant to the test. +* Fixtures and Test Suites:: How ERT differs from tools for other languages. + +@end detailmenu +@end menu + +@node Introduction, How to Run Tests, Top, Top +@chapter Introduction + +ERT allows you to define @emph{tests} in addition to functions, +macros, variables, and the other usual Lisp constructs. Tests are +simply Lisp code --- code that invokes other code and checks whether +it behaves as expected. + +ERT keeps track of the tests that are defined and provides convenient +commands to run them to verify whether the definitions that are +currently loaded in Emacs pass the tests. + +Some Lisp files have comments like the following (adapted from the +package @code{pp.el}): + +@lisp +;; (pp-to-string '(quote quote)) ; expected: "'quote" +;; (pp-to-string '((quote a) (quote b))) ; expected: "('a 'b)\n" +;; (pp-to-string '('a 'b)) ; same as above +@end lisp + +The code contained in these comments can be evaluated from time to +time to compare the output with the expected output. ERT formalizes +this and introduces a common convention, which simplifies Emacs +development, since programmers no longer have to manually find and +evaluate such comments. + +An ERT test definition equivalent to the above comments is this: + +@lisp +(ert-deftest pp-test-quote () + "Tests the rendering of `quote' symbols in `pp-to-string'." + (should (equal (pp-to-string '(quote quote)) "'quote")) + (should (equal (pp-to-string '((quote a) (quote b))) "('a 'b)\n")) + (should (equal (pp-to-string '('a 'b)) "('a 'b)\n"))) +@end lisp + +If you know @code{defun}, the syntax of @code{ert-deftest} should look +familiar: This example defines a test named @code{pp-test-quote} that +will pass if the three calls to @code{equal} all return true +(non-nil). + +@code{should} is a macro with the same meaning as @code{assert} but +better error reporting. @xref{The @code{should} Macro}. + +Each test should have a name that describes what functionality the +test tests. Test names can be chosen arbitrarily --- they are in a +namespace separate from functions and variables --- but should follow +the usual Emacs Lisp convention of having a prefix that indicates +which package they belong to. Test names are displayed by ERT when +reporting failures and can be used when selecting which tests to run. + +The empty parentheses @code{()} in the first line don't currently have +any meaning and are reserved for future extension. They also make +@code{ert-deftest}'s syntax more similar to @code{defun}. + +The docstring describes what feature this test tests. When running +tests interactively, the first line of the docstring is displayed for +tests that fail, so it is good if the first line makes sense on its +own. + +The body of a test can be arbitrary Lisp code. It should have as few +side effects as possible; each test should be written to clean up +after itself, leaving Emacs in the same state as it was before the +test. Tests should clean up even if they fail. @xref{Tests and Their +Environment}. + + +@node How to Run Tests, How to Write Tests, Introduction, Top +@chapter How to Run Tests + +You can run tests either in the Emacs you are working in, or on the +command line in a separate Emacs process in batch mode (i.e., with no +user interface). The former mode is convenient during interactive +development, the latter is useful to make sure that tests pass +independently of your customizations, allows tests to be invoked from +makefiles and scripts to be written that run tests in several +different Emacs versions. + +@menu +* Running Tests Interactively:: Run tests in your current Emacs. +* Running Tests in Batch Mode:: Run tests in emacs -Q. +* Test Selectors:: Choose which tests to run. +@end menu + + +@node Running Tests Interactively, Running Tests in Batch Mode, How to Run Tests, How to Run Tests +@section Running Tests Interactively + +You can run the tests that are currently defined in your Emacs with +the command @kbd{@kbd{M-x} ert @kbd{RET} t @kbd{RET}}. ERT will pop +up a new buffer, the ERT results buffer, showing the results of the +tests run. It looks like this: + +@example +Selector: t +Passed: 31 +Failed: 2 (2 unexpected) +Total: 33/33 + +Started at: 2008-09-11 08:39:25-0700 +Finished. +Finished at: 2008-09-11 08:39:27-0700 + +FF............................... + +F addition-test + (ert-test-failed + ((should + (= + (+ 1 2) + 4)) + :form + (= 3 4) + :value nil)) + +F list-test + (ert-test-failed + ((should + (equal + (list 'a 'b 'c) + '(a b d))) + :form + (equal + (a b c) + (a b d)) + :value nil :explanation + (list-elt 2 + (different-atoms c d)))) +@end example + +At the top, there is a summary of the results: We ran all tests in the +current Emacs (@code{Selector: t}), 31 of them passed, and 2 failed +unexpectedly. @xref{Expected Failures}, for an explanation of the +term @emph{unexpected} in this context. + +The line of dots and @code{F}s is a progress bar where each character +represents one test; it fills while the tests are running. A dot +means that the test passed, an @code{F} means that it failed. Below +the progress bar, ERT shows details about each test that had an +unexpected result. In the example above, there are two failures, both +due to failed @code{should} forms. @xref{Understanding Explanations}, +for more details. + +In the ERT results buffer, @kbd{TAB} and @kbd{S-TAB} cycle between +buttons. Each name of a function or macro in this buffer is a button; +moving point to it and typing @kbd{RET} jumps to its definition. + +Pressing @kbd{r} re-runs the test near point on its own. Pressing +@kbd{d} re-runs it with the debugger enabled. @kbd{.} jumps to the +definition of the test near point (@kbd{RET} has the same effect if +point is on the name of the test). On a failed test, @kbd{b} shows +the backtrace of the failure. + +@kbd{l} shows the list of @code{should} forms executed in the test. +If any messages were generated (with the Lisp function @code{message}) +in a test or any of the code that it invoked, @kbd{m} will show them. + +By default, long expressions in the failure details are abbreviated +using @code{print-length} and @code{print-level}. Pressing @kbd{L} +while point is on a test failure will increase the limits to show more +of the expression. + + +@node Running Tests in Batch Mode, Test Selectors, Running Tests Interactively, How to Run Tests +@section Running Tests in Batch Mode + +ERT supports automated invocations from the command line or from +scripts or makefiles. There are two functions for this purpose, +@code{ert-run-tests-batch} and @code{ert-run-tests-batch-and-exit}. +They can be used like this: + +@example +emacs -batch -L /path/to/ert -l ert.el -l my-tests.el -f ert-run-tests-batch-and-exit +@end example + +This command will start up Emacs in batch mode, load ERT, load +@code{my-tests.el}, and run all tests defined in it. It will exit +with a zero exit status if all tests passed, or nonzero if any tests +failed or if anything else went wrong. It will also print progress +messages and error diagnostics to standard output. + +You may need additional @code{-L} flags to ensure that +@code{my-tests.el} and all the files that it requires are on your +@code{load-path}. + + +@node Test Selectors, , Running Tests in Batch Mode, How to Run Tests +@section Test Selectors + +Functions like @code{ert} accept a @emph{test selector}, a Lisp +expression specifying a set of tests. Test selector syntax is similar +to Common Lisp's type specifier syntax: + +@itemize +@item @code{nil} selects no tests. +@item @code{t} selects all tests. +@item @code{:new} selects all tests that have not been run yet. +@item @code{:failed} and @code{:passed} select tests according to their most recent result. +@item @code{:expected}, @code{:unexpected} select tests according to their most recent result. +@item A string selects all tests that have a name that matches the string, a regexp. +@item A test selects that test. +@item A symbol selects the test that the symbol names. +@item @code{(member TESTS...)} selects TESTS, a list of tests or symbols naming tests. +@item @code{(eql TEST)} selects TEST, a test or a symbol naming a test. +@item @code{(and SELECTORS...)} selects the tests that match all SELECTORS. +@item @code{(or SELECTORS...)} selects the tests that match any SELECTOR. +@item @code{(not SELECTOR)} selects all tests that do not match SELECTOR. +@item @code{(tag TAG)} selects all tests that have TAG on their tags list. +@item @code{(satisfies PREDICATE)} Selects all tests that satisfy PREDICATE. +@end itemize + +Selectors that are frequently useful when selecting tests to run +include @code{t} to run all tests that are currently defined in Emacs, +@code{"^foo-"} to run all tests in package @code{foo} --- this assumes +that package @code{foo} uses the prefix @code{foo-} for its test names +---, result-based selectors such as @code{(or :new :unexpected)} to +run all tests that have either not run yet or that had an unexpected +result in the last run, and tag-based selectors such as @code{(not +(tag :causes-redisplay))} to run all tests that are not tagged +@code{:causes-redisplay}. + + +@node How to Write Tests, How to Debug Tests, How to Run Tests, Top +@chapter How to Write Tests + +ERT lets you define tests in the same way you define functions. You +can type @code{ert-deftest} forms in a buffer and evaluate them there +with @code{eval-defun} or @code{compile-defun}, or you can save the +file and load it, optionally byte-compiling it first. + +Just like @code{find-function} is only able to find where a function +was defined if the function was loaded from a file, ERT is only able +to find where a test was defined if the test was loaded from a file. + + +@menu +* The @code{should} Macro:: A powerful way to express assertions. +* Expected Failures:: Tests for known bugs. +* Tests and Their Environment:: Don't depend on customizations; no side effects. +* Useful Techniques:: Some examples. +@end menu + +@node The @code{should} Macro, Expected Failures, How to Write Tests, How to Write Tests +@section The @code{should} Macro + +Test bodies can include arbitrary code; but to be useful, they need to +have checks whether the code being tested (or @emph{code under test}) +does what it is supposed to do. The macro @code{should} is similar to +@code{assert} from the cl package, but analyzes its argument form and +records information that ERT can display to help debugging. + +This test definition + +@lisp +(ert-deftest addition-test () + (should (= (+ 1 2) 4))) +@end lisp + +will produce this output when run via @kbd{M-x ert}: + +@example +F addition-test + (ert-test-failed + ((should + (= + (+ 1 2) + 4)) + :form + (= 3 4) + :value nil)) +@end example + +In this example, @code{should} recorded the fact that (= (+ 1 2) 4) +reduced to (= 3 4) before it reduced to nil. When debugging why the +test failed, it helps to know that the function @code{+} returned 3 +here. ERT records the return value for any predicate called directly +within @code{should}. + +In addition to @code{should}, ERT provides @code{should-not}, which +checks that the predicate returns nil, and @code{should-error}, which +checks that the form called within it signals an error. An example +use of @code{should-error}: + +@lisp +(ert-deftest test-divide-by-zero () + (should-error (/ 1 0) + :type 'arith-error)) +@end lisp + +This checks that dividing one by zero signals an error of type +@code{arith-error}. The @code{:type} argument to @code{should-error} +is optional; if absent, any type of error is accepted. +@code{should-error} returns an error description of the error that was +signalled, to allow additional checks to be made. The error +description has the format @code{(ERROR-SYMBOL . DATA)}. + +There is no @code{should-not-error} macro since tests that signal an +error fail anyway, so @code{should-not-error} is effectively the +default. + +@xref{Understanding Explanations}, for more details on what +@code{should} reports. + + +@node Expected Failures, Tests and Their Environment, The @code{should} Macro, How to Write Tests +@section Expected Failures + +Some bugs are complicated to fix or not very important and are left as +@emph{known bugs}. If there is a test case that triggers the bug and +fails, ERT will alert you of this failure every time you run all +tests. For known bugs, this alert is a distraction. The way to +suppress it is to add @code{:expected-result :failed} to the test +definition: + +@lisp +(ert-deftest future-bug () + "Test `time-forward' with negative arguments. +Since this functionality isn't implemented yet, the test is known to fail." + :expected-result :failed + (time-forward -1)) +@end lisp + +ERT will still display a small @code{f} in the progress bar as a +reminder that there is a known bug, and will count the test as failed, +but it will be quiet about it otherwise. + +An alternative to marking the test as a known failure this way is to +delete the test. This is a good idea if there is no intent to fix it, +i.e., if the behavior that was formerly considered a bug has become an +accepted feature. + +In general, however, it can be useful to keep tests that are known to +fail. If someone wants to fix the bug, they will have a very good +starting point: an automated test case that reproduces the bug. This +makes it much easier to fix the bug, demonstrate that it is fixed, and +prevent future regressions. + +ERT displays the same kind of alerts for tests that pass unexpectedly +that it displays for unexpected failures. This way, if you make code +changes that happen to fix a bug that you weren't aware of, you will +know to remove the @code{:expected-result} clause of that test and +close the corresponding bug report, if any. + +Since @code{:expected-result} evaluates its argument when the test is +loaded, tests can be marked as known failures only on certain Emacs +versions, specific architectures, etc.: + +@lisp +(ert-deftest foo () + "A test that is expected to fail on Emacs 23 but succeed elsewhere." + :expected-result (if (string-match "GNU Emacs 23[.]" (emacs-version)) + :failed + :passed) + ...) +@end lisp + + +@node Tests and Their Environment, Useful Techniques, Expected Failures, How to Write Tests +@section Tests and Their Environment + +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' 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 @code{let}-bind +all such settings 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' current state, such as +on the current buffer or window configuration, the test should create +a temporary buffer for the code to manipulate (using +@code{with-temp-buffer}), or save and restore the window configuration +(using @code{save-window-excursion}), respectively. For aspects of +the state that can not be preserved with such macros, cleanup should +be performed with @code{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 +@code{message} and similar logging; tests should not bother restoring +the @code{*Message*} buffer to its original state. + +The above guidelines imply that tests should avoid calling highly +customizable commands such as @code{find-file}, except, of course, if +such commands are what they want to test. The exact behavior of +@code{find-file} depends on many settings such as +@code{find-file-wildcards}, @code{enable-local-variables}, and +@code{auto-mode-alist}. It is difficult to write a meaningful test if +its behavior can be affected by so many external factors. Also, +@code{find-file} has side effects that are hard to predict and thus +hard to undo: It may create a new buffer or may reuse an existing +buffer if one is already visiting the requested file; and it runs +@code{find-file-hook}, which can have arbitrary side effects. + +Instead, it is better to use lower-level mechanisms with simple and +predictable semantics like @code{with-temp-buffer}, @code{insert} or +@code{insert-file-contents-literally}, and activating the desired mode +by calling the corresponding function directly --- after binding the +hook variables to nil. This avoids the above problems. + + +@node Useful Techniques, , Tests and Their Environment, How to Write Tests +@section Useful Techniques when Writing Tests + +Testing simple functions that have no side effects and no dependencies +on their environment is easy. Such tests often look like this: + +@lisp +(ert-deftest ert-test-mismatch () + (should (eql (ert--mismatch "" "") nil)) + (should (eql (ert--mismatch "" "a") 0)) + (should (eql (ert--mismatch "a" "a") nil)) + (should (eql (ert--mismatch "ab" "a") 1)) + (should (eql (ert--mismatch "Aa" "aA") 0)) + (should (eql (ert--mismatch '(a b c) '(a b d)) 2))) +@end lisp + +This test calls the function @code{ert--mismatch} several times with +various combinations of arguments and compares the return value to the +expected return value. (Some programmers prefer @code{(should (eql +EXPECTED ACTUAL))} over the @code{(should (eql ACTUAL EXPECTED))} +shown here. ERT works either way.) + +Here's a more complicated test: + +@lisp +(ert-deftest ert-test-record-backtrace () + (let ((test (make-ert-test :body (lambda () (ert-fail "foo"))))) + (let ((result (ert-run-test test))) + (should (ert-test-failed-p result)) + (with-temp-buffer + (ert--print-backtrace (ert-test-failed-backtrace result)) + (goto-char (point-min)) + (end-of-line) + (let ((first-line (buffer-substring-no-properties (point-min) (point)))) + (should (equal first-line " signal(ert-test-failed (\"foo\"))"))))))) +@end lisp + +This test creates a test object using @code{make-ert-test} whose body +will immediately signal failure. It then runs that test and asserts +that it fails. Then, it creates a temporary buffer and invokes +@code{ert--print-backtrace} to print the backtrace of the failed test +to the current buffer. Finally, it extracts the first line from the +buffer and asserts that it matches what we expect. It uses +@code{buffer-substring-no-properties} and @code{equal} to ignore text +properties; for a test that takes properties into account, +@code{buffer-substring} and @code{ert-equal-including-properties} +could be used instead. + +The reason why this test only checks the first line of the backtrace +is that the remainder of the backtrace is dependent on ERT's internals +as well as whether the code is running interpreted or compiled. By +looking only at the first line, the test checks a useful property +--- that the backtrace correctly captures the call to @code{signal} that +results from the call to @code{ert-fail} --- without being brittle. + +This example also shows that writing tests is much easier if the code +under test was structured with testing in mind. + +For example, if @code{ert-run-test} accepted only symbols that name +tests rather than test objects, the test would need a name for the +failing test, which would have to be a temporary symbol generated with +@code{make-symbol}, to avoid side effects on Emacs' state. Choosing +the right interface for @code{ert-run-tests} allows the test to be +simpler. + +Similarly, if @code{ert--print-backtrace} printed the backtrace to a +buffer with a fixed name rather than the current buffer, it would be +much harder for the test to undo the side effect. Of course, some +code somewhere needs to pick the buffer name. But that logic is +independent of the logic that prints backtraces, and keeping them in +separate functions allows us to test them independently. + +A lot of code that you will encounter in Emacs was not written with +testing in mind. Sometimes, the easiest way to write tests for such +code is to restructure the code slightly to provide better interfaces +for testing. Usually, this makes the interfaces easier to use as +well. + + +@node How to Debug Tests, Extending ERT, How to Write Tests, Top +@chapter How to Debug Tests + +This section describes how to use ERT's features to understand why +a test failed. + + +@menu +* Understanding Explanations:: How ERT gives details on why an assertion failed. +* Interactive Debugging:: Tools available in the ERT results buffer. +@end menu + + +@node Understanding Explanations, Interactive Debugging, How to Debug Tests, How to Debug Tests +@section Understanding Explanations + +Failed @code{should} forms are reported like this: + +@example +F addition-test + (ert-test-failed + ((should + (= + (+ 1 2) + 4)) + :form + (= 3 4) + :value nil)) +@end example + +ERT shows what the @code{should} expression looked like and what +values its subexpressions had: The source code of the assertion was +@code{(should (= (+ 1 2) 4))}, which applied the function @code{=} to +the arguments @code{3} and @code{4}, resulting in the value +@code{nil}. In this case, the test is wrong; it should expect 3 +rather than 4. + +If a predicate like @code{equal} is used with @code{should}, ERT +provides a so-called @emph{explanation}: + +@example +F list-test + (ert-test-failed + ((should + (equal + (list 'a 'b 'c) + '(a b d))) + :form + (equal + (a b c) + (a b d)) + :value nil :explanation + (list-elt 2 + (different-atoms c d)))) +@end example + +In this case, the function @code{equal} was applied to the arguments +@code{(a b c)} and @code{(a b d)}. ERT's explanation shows that +the item at index 2 differs between the two lists; in one list, it is +the atom c, in the other, it is the atom d. + +In simple examples like the above, the explanation is unnecessary. +But in cases where the difference is not immediately apparent, it can +save time: + +@example +F test1 + (ert-test-failed + ((should + (equal x y)) + :form + (equal a a) + :value nil :explanation + (different-symbols-with-the-same-name a a))) +@end example + +ERT only provides explanations for predicates that have an explanation +function registered. @xref{Defining Explanation Functions}. + + +@node Interactive Debugging, , Understanding Explanations, How to Debug Tests +@section Interactive Debugging + +Debugging failed tests works essentially the same way as debugging any +other problems with Lisp code. Here are a few tricks specific to +tests: + +@itemize +@item Re-run the failed test a few times to see if it fails in the same way +each time. It's good to find out whether the behavior is +deterministic before spending any time looking for a cause. In the +ERT results buffer, @kbd{r} re-runs the selected test. + +@item Use @kbd{.} to jump to the source code of the test to find out what +exactly it does. Perhaps the test is broken rather than the code +under test. + +@item If the test contains a series of @code{should} forms and you can't +tell which one failed, use @kbd{l}, which shows you the list of all +@code{should} forms executed during the test before it failed. + +@item Use @kbd{b} to view the backtrace. You can also use @kbd{d} to re-run +the test with debugging enabled, this will enter the debugger and show +the backtrace as well; but the top few frames shown there will not be +relevant to you since they are ERT's own debugger hook. @kbd{b} +strips them out, so it is more convenient. + +@item If the test or the code under testing prints messages using +@code{message}, use @kbd{m} to see what messages it printed before it +failed. This can be useful to figure out how far it got. + +@item You can instrument tests for debugging the same way you instrument +@code{defun}s for debugging --- go to the source code of the test and +type @kbd{@kbd{C-u} @kbd{C-M-x}}. Then, go back to the ERT buffer and +re-run the test with @kbd{r} or @kbd{d}. + +@item If you have been editing and rearranging tests, it is possible that +ERT remembers an old test that you have since renamed or removed --- +renamings or removals of definitions in the source code leave around a +stray definition under the old name in the running process, this is a +common problem in Lisp. In such a situation, hit @kbd{D} to let ERT +forget about the obsolete test. +@end itemize + + +@node Extending ERT, Other Testing Concepts, How to Debug Tests, Top +@chapter Extending ERT + +There are several ways to add functionality to ERT. + +@menu +* Defining Explanation Functions:: Teach ERT about more predicates. +* Low-Level Functions for Working with Tests:: Use ERT's data for your purposes. +@end menu + + +@node Defining Explanation Functions, Low-Level Functions for Working with Tests, Extending ERT, Extending ERT +@section Defining Explanation Functions + +The explanation function for a predicate is a function that takes the +same arguments as the predicate and returns an @emph{explanation}. +The explanation should explain why the predicate, when invoked with +the arguments given to the explanation function, returns the value +that it returns. The explanation can be any object but should have a +comprehensible printed representation. If the return value of the +predicate needs no explanation for a given list of arguments, the +explanation function should return nil. + +To associate an explanation function with a predicate, add the +property @code{ert-explainer} to the symbol that names the predicate. +The value of the property should be the symbol that names the +explanation function. + + +@node Low-Level Functions for Working with Tests, , Defining Explanation Functions, Extending ERT +@section Low-Level Functions for Working with Tests + +Both @code{ert-run-tests-interactively} and @code{ert-run-tests-batch} +are implemented on top of the lower-level test handling code in the +sections named ``Facilities for running a single test'', ``Test +selectors'', and ``Facilities for running a whole set of tests''. + +If you want to write code that works with ERT tests, you should take a +look at this lower-level code. Symbols that start with @code{ert--} +are internal to ERT, those that start with @code{ert-} but not +@code{ert--} are meant to be usable by other code. But there is no +mature API yet. + +Contributions to ERT are welcome. + + +@node Other Testing Concepts, , Extending ERT, Top +@chapter Other Testing Concepts + +For information on mocks, stubs, fixtures, or test suites, see below. + + +@menu +* Mocks and Stubs:: Stubbing out code that is irrelevant to the test. +* Fixtures and Test Suites:: How ERT differs from tools for other languages. +@end menu + +@node Mocks and Stubs, Fixtures and Test Suites, Other Testing Concepts, Other Testing Concepts +@section Other Tools for Emacs Lisp + +Stubbing out functions or using so-called @emph{mocks} can make it +easier to write tests. See +@url{http://en.wikipedia.org/wiki/Mock_object} for an explanation of +the corresponding concepts in object-oriented languages. + +ERT does not have built-in support for mocks or stubs. The package +@code{el-mock} (see @url{http://www.emacswiki.org/emacs/el-mock.el}) +offers mocks for Emacs Lisp and can be used in conjunction with ERT. + + +@node Fixtures and Test Suites, , Mocks and Stubs, Other Testing Concepts +@section Fixtures and Test Suites + +In many ways, ERT is similar to frameworks for other languages like +SUnit or JUnit. However, two features commonly found in such +frameworks are notably absent from ERT: fixtures and test suites. + +Fixtures, as used e.g. in SUnit or JUnit, are mainly used to provide +an environment for a set of tests, and consist of set-up and tear-down +functions. + +While fixtures are a useful syntactic simplification in other +languages, this does not apply to Lisp, where higher-order functions +and `unwind-protect' are available. One way to implement and use a +fixture in ERT is + +@lisp +(defun my-fixture (body) + (unwind-protect + (progn [set up] + (funcall body)) + [tear down])) + +(ert-deftest my-test () + (my-fixture + (lambda () + [test code]))) +@end lisp + +(Another way would be a @code{with-my-fixture} macro.) This solves +the set-up and tear-down part, and additionally allows any test +to use any combination of fixtures, so it is more flexible than what +other tools typically allow. + +If the test needs access to the environment the fixture sets up, the +fixture can be modified to pass arguments to the body. + +These are well-known Lisp techniques. Special syntax for them could +be added but would provide only a minor simplification. + +(If you are interested in such syntax, note that splitting set-up and +tear-down into separate functions, like *Unit tools usually do, makes +it impossible to establish dynamic `let' bindings as part of the +fixture. So, blindly imitating the way fixtures are implemented in +other languages would be counter-productive in Lisp.) + +The purpose of test suites is to group related tests together. + +The most common use of this is to run just the tests for one +particular module. Since symbol prefixes are the usual way of +separating module namespaces in Emacs Lisp, test selectors already +solve this by allowing regexp matching on test names; e.g., the +selector "^ert-" selects ERT's self-tests. + +Other uses include grouping tests by their expected execution time to +run quick tests during interactive development and slow tests less +frequently. This can be achieved with the @code{:tag} argument to +@code{ert-deftest} and @code{tag} test selectors. + +@bye + +@c LocalWords: ERT Hagelberg Ohler JUnit namespace docstring ERT's +@c LocalWords: backtrace makefiles workflow backtraces API SUnit +@c LocalWords: subexpressions |