diff options
| author | Michal Nazarewicz | 2016-02-23 14:44:56 +1100 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2016-02-23 14:44:56 +1100 |
| commit | 6f0498bf290cbe066036f8bfe0d09ad99ddaab03 (patch) | |
| tree | 391e4f25adbbe7d88e1f968bbef54ddeb1dcda84 | |
| parent | 96e32bbb736ec6e0a7278ae864098c7c812b05a4 (diff) | |
| download | emacs-6f0498bf290cbe066036f8bfe0d09ad99ddaab03.tar.gz emacs-6f0498bf290cbe066036f8bfe0d09ad99ddaab03.zip | |
ert-with-function-mocked: New macro
* lisp/emacs-lisp/ert-x.el (ert-with-function-mocked): New macro which
allows evaluating code while particular function is replaced with
a mock. The original definition of said function is restored once the
macro finishes.
| -rw-r--r-- | etc/NEWS | 3 | ||||
| -rw-r--r-- | lisp/emacs-lisp/ert-x.el | 40 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/ert-x-tests.el | 43 |
3 files changed, 86 insertions, 0 deletions
| @@ -104,6 +104,9 @@ different group ID. | |||
| 104 | ** Autoload files can be generated without timestamps, | 104 | ** Autoload files can be generated without timestamps, |
| 105 | by setting `autoload-timestamps' to nil. | 105 | by setting `autoload-timestamps' to nil. |
| 106 | 106 | ||
| 107 | ** `ert-with-function-mocked' of 'ert-x package allows mocking of functions | ||
| 108 | in unit tests. | ||
| 109 | |||
| 107 | 110 | ||
| 108 | * Changes in Emacs 25.2 on Non-Free Operating Systems | 111 | * Changes in Emacs 25.2 on Non-Free Operating Systems |
| 109 | 112 | ||
diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el index 2a2418fa7d2..eb10c845d3f 100644 --- a/lisp/emacs-lisp/ert-x.el +++ b/lisp/emacs-lisp/ert-x.el | |||
| @@ -285,6 +285,46 @@ BUFFER defaults to current buffer. Does not modify BUFFER." | |||
| 285 | (kill-buffer clone))))))) | 285 | (kill-buffer clone))))))) |
| 286 | 286 | ||
| 287 | 287 | ||
| 288 | (defmacro ert-with-function-mocked (name mock &rest body) | ||
| 289 | "Mocks function NAME with MOCK and run BODY. | ||
| 290 | |||
| 291 | Once BODY finishes (be it normally by returning a value or | ||
| 292 | abnormally by throwing or signalling), the old definition of | ||
| 293 | function NAME is restored. | ||
| 294 | |||
| 295 | BODY may further change the mock with `fset'. | ||
| 296 | |||
| 297 | If MOCK is nil, the function NAME is mocked with a function | ||
| 298 | `ert-fail'ing when called. | ||
| 299 | |||
| 300 | For example: | ||
| 301 | |||
| 302 | ;; Regular use, function is mocked inside the BODY: | ||
| 303 | (should (eq 2 (+ 1 1))) | ||
| 304 | (ert-with-function-mocked ((+ (lambda (a b) (- a b)))) | ||
| 305 | (should (eq 0 (+ 1 1)))) | ||
| 306 | (should (eq 2 (+ 1 1))) | ||
| 307 | |||
| 308 | ;; Macro correctly recovers from a throw or signal: | ||
| 309 | (should | ||
| 310 | (catch 'done | ||
| 311 | (ert-with-function-mocked ((+ (lambda (a b) (- a b)))) | ||
| 312 | (should (eq 0 (+ 1 1)))) | ||
| 313 | (throw 'done t))) | ||
| 314 | (should (eq 2 (+ 1 1))) | ||
| 315 | " | ||
| 316 | (declare (indent 2)) | ||
| 317 | (let ((old-var (make-symbol "old-var")) | ||
| 318 | (mock-var (make-symbol "mock-var"))) | ||
| 319 | `(let ((,old-var (symbol-function (quote ,name))) (,mock-var ,mock)) | ||
| 320 | (fset (quote ,name) | ||
| 321 | (or ,mock-var (lambda (&rest _) | ||
| 322 | (ert-fail (concat "`" ,(symbol-name name) | ||
| 323 | "' unexpectedly called."))))) | ||
| 324 | (unwind-protect | ||
| 325 | (progn ,@body) | ||
| 326 | (fset (quote ,name) ,old-var))))) | ||
| 327 | |||
| 288 | (provide 'ert-x) | 328 | (provide 'ert-x) |
| 289 | 329 | ||
| 290 | ;;; ert-x.el ends here | 330 | ;;; ert-x.el ends here |
diff --git a/test/lisp/emacs-lisp/ert-x-tests.el b/test/lisp/emacs-lisp/ert-x-tests.el index ef8642aebfb..a2665e7c390 100644 --- a/test/lisp/emacs-lisp/ert-x-tests.el +++ b/test/lisp/emacs-lisp/ert-x-tests.el | |||
| @@ -275,6 +275,49 @@ desired effect." | |||
| 275 | (should (equal (c x) (lisp x)))))) | 275 | (should (equal (c x) (lisp x)))))) |
| 276 | 276 | ||
| 277 | 277 | ||
| 278 | (defun ert--dummy-id (a) | ||
| 279 | "Identity function. Used for tests only." | ||
| 280 | a) | ||
| 281 | |||
| 282 | (ert-deftest ert-with-function-mocked () | ||
| 283 | (let ((mock-id (lambda (_) 21))) | ||
| 284 | (should (eq 42 (ert--dummy-id 42))) | ||
| 285 | |||
| 286 | (ert-with-function-mocked ert--dummy-id nil | ||
| 287 | (fset 'ert--dummy-id mock-id) | ||
| 288 | (should (eq 21 (ert--dummy-id 42)))) | ||
| 289 | (should (eq 42 (ert--dummy-id 42))) | ||
| 290 | |||
| 291 | (ert-with-function-mocked ert--dummy-id mock-id | ||
| 292 | (should (eq 21 (ert--dummy-id 42)))) | ||
| 293 | (should (eq 42 (ert--dummy-id 42))) | ||
| 294 | |||
| 295 | (should | ||
| 296 | (catch 'exit | ||
| 297 | (ert-with-function-mocked ert--dummy-id mock-id | ||
| 298 | (should (eq 21 (ert--dummy-id 42)))) | ||
| 299 | (throw 'exit t))) | ||
| 300 | (should (eq 42 (ert--dummy-id 42))) | ||
| 301 | |||
| 302 | (should | ||
| 303 | (string= "Foo" | ||
| 304 | (condition-case err | ||
| 305 | (progn | ||
| 306 | (ert-with-function-mocked ert--dummy-id mock-id | ||
| 307 | (should (eq 21 (ert--dummy-id 42)))) | ||
| 308 | (user-error "Foo")) | ||
| 309 | (user-error (cadr err))))) | ||
| 310 | (should (eq 42 (ert--dummy-id 42))) | ||
| 311 | |||
| 312 | (should | ||
| 313 | (string= "`ert--dummy-id' unexpectedly called." | ||
| 314 | (condition-case err | ||
| 315 | (ert-with-function-mocked ert--dummy-id nil | ||
| 316 | (ert--dummy-id 42)) | ||
| 317 | (ert-test-failed (cadr err))))) | ||
| 318 | (should (eq 42 (ert--dummy-id 42))))) | ||
| 319 | |||
| 320 | |||
| 278 | (provide 'ert-x-tests) | 321 | (provide 'ert-x-tests) |
| 279 | 322 | ||
| 280 | ;;; ert-x-tests.el ends here | 323 | ;;; ert-x-tests.el ends here |