Files
gh-hugoduncan-library-skill…/skills/ert/QUICK_REFERENCE.md
2025-11-29 18:47:18 +08:00

5.3 KiB

ERT Quick Reference

Test Definition

(ert-deftest NAME ()
  [DOCSTRING]
  [:tags (TAG...)]
  BODY...)

Example:

(ert-deftest my-pkg-test-feature ()
  "Test feature implementation."
  :tags '(unit quick)
  (should (my-pkg-feature-works)))

Assertions

Macro Purpose Example
(should FORM) Assert true (should (= 4 (+ 2 2)))
(should-not FORM) Assert false (should-not (zerop 5))
(should-error FORM [:type TYPE]) Assert error (should-error (/ 1 0) :type 'arith-error)

Running Tests

Interactive

M-x ert RET SELECTOR RET

Common selectors:

  • t - All tests
  • "^my-pkg-" - Tests matching regex
  • :tag quick - Tests with tag
  • :failed - Failed tests from last run
  • (not :tag slow) - Exclude slow tests

Batch Mode

# Run all tests
emacs -batch -l ert -l tests.el -f ert-run-tests-batch-and-exit

# Run specific tests
emacs -batch -l ert -l tests.el \
  --eval '(ert-run-tests-batch-and-exit "^test-feature-")'

# Quiet mode
emacs -batch -l ert -l tests.el \
  --eval '(let ((ert-quiet t)) (ert-run-tests-batch-and-exit))'

Interactive Debugging Keys

In *ert* results buffer:

Key Command Description
. Jump to definition Open test source code
d Debug test Re-run with debugger
b Show backtrace Display failure backtrace
r Re-run test Re-run test at point
R Re-run all Re-run all tests
l Show assertions Display executed should forms
m Show messages Display test messages
TAB Expand/collapse Toggle test details

Test Environment Patterns

Temporary Buffer

(with-temp-buffer
  (insert "content")
  (my-function)
  (should (string= (buffer-string) "expected")))

Cleanup

(let ((resource (create-resource)))
  (unwind-protect
      (should (use-resource resource))
    (cleanup-resource resource)))

Local Variables

(let ((my-var 'test-value))
  (should (eq 'test-value (get-my-var))))

Mocking

With cl-letf

(cl-letf (((symbol-function 'external-fn)
           (lambda () "mocked")))
  (should (string= "mocked" (calls-external-fn))))

With flet (older style)

(require 'cl)
(flet ((external-fn () "mocked"))
  (should (string= "mocked" (calls-external-fn))))

Skip Tests

;; Skip if condition false
(skip-unless (featurep 'some-feature))

;; Skip if condition true
(skip-when (eq system-type 'windows-nt))

Test Organization

Naming Convention

;; Format: package-test-feature
(ert-deftest my-package-test-parsing () ...)
(ert-deftest my-package-test-validation () ...)

Tags

(ert-deftest test-quick ()
  :tags '(quick unit)
  ...)

(ert-deftest test-slow ()
  :tags '(slow integration)
  ...)

;; Run: M-x ert RET :tag quick RET
;; Run: M-x ert RET (not :tag slow) RET

Fixture Pattern

(defun with-test-fixture (body)
  "Execute BODY within test fixture."
  (let ((setup (do-setup)))
    (unwind-protect
        (funcall body setup)
      (do-teardown setup))))

(ert-deftest my-test ()
  (with-test-fixture
   (lambda (fixture)
     (should (test-with fixture)))))

Common Test Patterns

Testing Errors

;; Any error
(should-error (/ 1 0))

;; Specific error type
(should-error (error "msg") :type 'error)
(should-error (/ 1 0) :type 'arith-error)

Testing Buffer State

(with-temp-buffer
  (my-mode)
  (should (eq major-mode 'my-mode))
  (should (local-variable-p 'my-mode-var)))

Testing Interactive Commands

(with-temp-buffer
  (insert "text")
  (goto-char (point-min))
  (call-interactively 'my-command)
  (should (= (point) 5)))

Testing Messages

(let ((messages))
  (cl-letf (((symbol-function 'message)
             (lambda (fmt &rest args)
               (push (apply #'format fmt args) messages))))
    (my-function)
    (should (member "Expected message" messages))))

Test File Template

;;; my-package-test.el --- Tests for my-package -*- lexical-binding: t -*-

;;; Commentary:
;; Test suite for my-package.

;;; Code:

(require 'ert)
(require 'my-package)

(ert-deftest my-package-test-basic ()
  "Test basic functionality."
  (should (my-package-function)))

(provide 'my-package-test)
;;; my-package-test.el ends here

Makefile Integration

.PHONY: test
test:
	emacs -batch -l ert \
	  -l my-package.el \
	  -l test/my-package-test.el \
	  -f ert-run-tests-batch-and-exit

CI/CD (GitHub Actions)

- name: Run tests
  run: |
    emacs -batch -l ert \
      -l my-package.el \
      -l test/my-package-test.el \
      -f ert-run-tests-batch-and-exit

Key Best Practices

  1. Name descriptively: package-test-feature format
  2. Use tags: Organize by speed/type
  3. Isolate environment: let, temp buffers
  4. Always cleanup: unwind-protect
  5. Mock I/O: Avoid filesystem/network
  6. Test errors: Use should-error
  7. One focus per test: Clear failures
  8. Add docstrings: Document test purpose

Resources