aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Nazarewicz2016-02-23 14:44:56 +1100
committerLars Ingebrigtsen2016-02-23 14:44:56 +1100
commit6f0498bf290cbe066036f8bfe0d09ad99ddaab03 (patch)
tree391e4f25adbbe7d88e1f968bbef54ddeb1dcda84
parent96e32bbb736ec6e0a7278ae864098c7c812b05a4 (diff)
downloademacs-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/NEWS3
-rw-r--r--lisp/emacs-lisp/ert-x.el40
-rw-r--r--test/lisp/emacs-lisp/ert-x-tests.el43
3 files changed, 86 insertions, 0 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 9e000be4a7a..04c1ee83aba 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -104,6 +104,9 @@ different group ID.
104** Autoload files can be generated without timestamps, 104** Autoload files can be generated without timestamps,
105by setting `autoload-timestamps' to nil. 105by setting `autoload-timestamps' to nil.
106 106
107** `ert-with-function-mocked' of 'ert-x package allows mocking of functions
108in 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
291Once BODY finishes (be it normally by returning a value or
292abnormally by throwing or signalling), the old definition of
293function NAME is restored.
294
295BODY may further change the mock with `fset'.
296
297If MOCK is nil, the function NAME is mocked with a function
298`ert-fail'ing when called.
299
300For 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