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

271 lines
5.3 KiB
Markdown

# ERT Quick Reference
## Test Definition
```elisp
(ert-deftest NAME ()
[DOCSTRING]
[:tags (TAG...)]
BODY...)
```
**Example:**
```elisp
(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
```elisp
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
```bash
# 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
```elisp
(with-temp-buffer
(insert "content")
(my-function)
(should (string= (buffer-string) "expected")))
```
### Cleanup
```elisp
(let ((resource (create-resource)))
(unwind-protect
(should (use-resource resource))
(cleanup-resource resource)))
```
### Local Variables
```elisp
(let ((my-var 'test-value))
(should (eq 'test-value (get-my-var))))
```
## Mocking
### With cl-letf
```elisp
(cl-letf (((symbol-function 'external-fn)
(lambda () "mocked")))
(should (string= "mocked" (calls-external-fn))))
```
### With flet (older style)
```elisp
(require 'cl)
(flet ((external-fn () "mocked"))
(should (string= "mocked" (calls-external-fn))))
```
## Skip Tests
```elisp
;; Skip if condition false
(skip-unless (featurep 'some-feature))
;; Skip if condition true
(skip-when (eq system-type 'windows-nt))
```
## Test Organization
### Naming Convention
```elisp
;; Format: package-test-feature
(ert-deftest my-package-test-parsing () ...)
(ert-deftest my-package-test-validation () ...)
```
### Tags
```elisp
(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
```elisp
(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
```elisp
;; 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
```elisp
(with-temp-buffer
(my-mode)
(should (eq major-mode 'my-mode))
(should (local-variable-p 'my-mode-var)))
```
### Testing Interactive Commands
```elisp
(with-temp-buffer
(insert "text")
(goto-char (point-min))
(call-interactively 'my-command)
(should (= (point) 5)))
```
### Testing Messages
```elisp
(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
```elisp
;;; 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
```makefile
.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)
```yaml
- 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
- **Manual:** `C-h i m ert RET`
- **Online:** https://www.gnu.org/software/emacs/manual/html_mono/ert.html
- **Source:** `lisp/emacs-lisp/ert.el`