(defun sort-by-string-length (list &optional asc-desc)
"Sort a LIST of strings by their length.
Apply direction by ASC-DESC value which could be 'asc' or 'desc'."
(progn
(fset 'direction
(if (equal asc-desc "asc") '> '<))
(sort list (lambda (a b)
(direction (length a) (length b))))))
ELISP: testing with ERT
Intro
In the past few weeks I’ve been working on a small project to get used to elisp programming, and I got to a point when I needed to make sure I wasn’t breaking anything in the process. There’re several solutions out there but the first I put my hands on was ERT.
What is ERT ?
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.
Writing a buggy function
In order to show how ERT works, I’m creating a buggy function. The function is supposed to sort a list of strings in ascending order by default. But in fact it returns by default the strings in descending order.
Writing tests
In order to make sure the function does what it claims to do, we should write a test. An ERT test normally looks like the following:
(ert-deftest name-of-the-test ()
(should ...)) ;; assertions
An ERT test uses the macro ert-deftest
to declare a new test. Then
it uses should
to check assertions results. There’re more
possibilities other than should
: should-not
or should-error
. I’m
talking about them a little bit later.
(ert-deftest test-sort-by-default () (1)
(should
(equal (sort-by-string-length '("a" "aaa" "aa")) '("a" "aa" "aaa"))))
(ert-deftest test-sort-by-desc () (2)
(should (equal (sort-by-string-length '("a" "aaa" "aa") "desc") '("aaa" "aa" "a"))))
1 | Tests default sort (ascending) by string length |
2 | Tests descending sort by string length |
Executing tests
You can execute ERT test in different ways, but the one I prefer is batch execution via command line. Is the one I think I would use in a CI environmnet.
Of course, you will have to install Emacs in your CI environment in order to be able to execute these tests. |
emacs -batch -l ert -l /path/to/file_containing_tests.el -f ert-run-tests-batch-and-exit
And because I’ve made a mistake with the default sorting this is what I got:
Running 2 tests (2018-11-18 16:08:14+0100)
passed 1/2 test-sort-by-default
Test test-sort-by-desc backtrace:
...
Test test-sort-by-desc condition:
(ert-test-failed
((should
(equal
(sort-by-string-length-ko ... "desc")
'...))
:form
(equal
("a" "aa" "aaa") (1)
("aaa" "aa" "a")) (2)
:value nil :explanation
(list-elt 0
(arrays-of-different-length 1 3 "a" "aaa" first-mismatch-at 1))))
FAILED 2/2 test-sort-by-desc
Ran 2 tests, 1 results as expected, 1 unexpected (2018-11-18 16:08:14+0100)
1 unexpected results:
FAILED test-sort-by-desc
1 | Expected |
2 | Actual result |
Asserts with should
ERT provides different macros to help you make your tests easier to
read and understand. The most common is should
.
(ert-deftest test-sum-is-commutative ()
(should (= (+ 1 2) (+ 2 1))))
Sometimes you may want to assert that the result is not what is
expected, for example to assert that the division operation is not
commutative. For that you can use should-not
:
(ert-deftest test-division-not-commutative ()
(should-not (= (/ 1 2) (/ 2 1))))
And I’m sure that at some point, you’ll need to assert that some
function is throwing an error under some circumstances. In that case
you can use should-error
:
(ert-deftest test-error ()
(should-error
(signal 'singularity-error nil)
:type 'singularity-error))
In this example I’m checking that the function throws an error and also that error is of type 'singularity-error.
:type is optional
|
Fixing function
Now it’s time to do the fix and make the test pass.
(defun sort-by-string-length (list &optional asc-desc)
"Sort a LIST of strings by their length.
Apply direction by ASC-DESC value which could be 'asc' or 'desc'."
(progn
(fset 'direction
(if (equal asc-desc "desc")
'>
'<))
(sort list (lambda (a b)
(direction (length a) (length b))))))
Running tests again shows the following output:
Running 2 tests (2018-11-18 16:38:00+0100)
passed 1/2 test-sort-by-default
passed 2/2 test-sort-by-desc
Ran 2 tests, 2 results as expected (2018-11-18 16:38:00+0100)
Now everything works as expected