271 lines
5.3 KiB
Markdown
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`
|