mocker.el-0.5.0/0000755000175000017500000000000013770565241013237 5ustar dogslegdogslegmocker.el-0.5.0/test/0000755000175000017500000000000013770565241014216 5ustar dogslegdogslegmocker.el-0.5.0/test/mocker-test.el0000644000175000017500000002141213770565241016775 0ustar dogslegdogsleg;;; mocker-tests.el --- tests for mocker.el ;; Copyright (C) 2011 Free Software Foundation, Inc. ;; Author: Yann Hodique ;; Keywords: lisp, testing ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; This file contains tests for mocker.el ;;; Code: (require 'cl-lib) (require 'mocker) (ert-deftest mocker-let-basic () (should (eq t (mocker-let () t)))) (ert-deftest mocker-let-single () (should (eq t (mocker-let ((foo () :records ((:output t)))) (foo))))) (ert-deftest mocker-let-single-implicit-records () (should (eq t (mocker-let ((foo () ((:output t)))) (foo))))) (ert-deftest mocker-let-no-record () (should-error (mocker-let ((foo () :records ())) (foo)) :type 'mocker-mock-error)) (ert-deftest mocker-let-multiple () (should (eq 42 (mocker-let ((foo () :records ((:output 6))) (bar () :records ((:output 7)))) (* (foo) (bar)))))) (ert-deftest mocker-let-nested () (should (eq 42 (mocker-let ((foo () :records ((:output 6)))) (mocker-let ((bar () :records ((:output 7)))) (* (foo) (bar))))))) (ert-deftest mocker-let-multiple-inputs () (should (eq 42 (mocker-let ((foo (x) :records ((:input '(1) :output 6))) (bar (x) :records ((:input '(2) :output 7)))) (* (foo 1) (bar 2)))))) (ert-deftest mocker-let-multiple-inputs-invalid () (should-error (mocker-let ((foo (x) :records ((:input '(1) :output 6))) (bar (x) :records ((:input '(2) :output 7)))) (* (foo 2) (bar 2))) :type 'mocker-record-error)) (ert-deftest mocker-let-single-input-matcher () (should (eq t (mocker-let ((foo (x) :records ((:input-matcher 'integerp :output t)))) (foo 4))))) (ert-deftest mocker-let-single-input-matcher-invalid () (should-error (mocker-let ((foo (x) :records ((:input-matcher 'integerp :output t)))) (foo t)) :type 'mocker-record-error)) (ert-deftest mocker-let-multiple-output-generator () (should (eq 2 (mocker-let ((foo (x) :records ((:input '(2) :output-generator (function identity)) (:input '(4) :output-generator (lambda (x) 0))))) (+ (foo 2) (foo 4)))))) (ert-deftest mocker-let-multiple-calls-min () (should (eq 4 (mocker-let ((foo (x) :records ((:input '(2) :output-generator (function identity) :min-occur 2)))) (+ (foo 2) (foo 2)))))) (ert-deftest mocker-let-multiple-calls-illimited () (should (eq 8 (mocker-let ((foo (x) :records ((:input '(2) :output-generator (function identity) :max-occur nil)))) (+ (foo 2) (foo 2) (foo 2) (foo 2)))))) (ert-deftest mocker-let-multiple-calls-exact () (should (eq 8 (mocker-let ((foo (x) :records ((:input '(2) :output-generator (function identity) :occur 4)))) (+ (foo 2) (foo 2) (foo 2) (foo 2)))))) (ert-deftest mocker-let-multiple-calls-multiple-records () (should (eq 12 (mocker-let ((foo (x) :records ((:input '(2) :output-generator (function identity) :max-occur 2) (:input '(2) :output-generator (lambda (x) (* 2 x)) :occur 2)))) (+ (foo 2) (foo 2) (foo 2) (foo 2)))))) (ert-deftest mocker-let-multiple-calls-multiple-same-records () (should (eq 8 (mocker-let ((foo (x) :records ((:input '(2) :output-generator (function identity) :max-occur 2) (:input '(2) :output-generator (function identity) :max-occur 2)))) (+ (foo 2) (foo 2) (foo 2) (foo 2)))))) (ert-deftest mocker-let-multiple-calls-unexpected () (should-error (mocker-let ((foo (x) :records ((:input '(2) :output-generator (function identity) :max-occur 2)))) (+ (foo 2) (foo 2) (foo 2) (foo 2)))) :type 'mocker-record-error) (ert-deftest mocker-let-multiple-calls-unexpected-exact () (should-error (mocker-let ((foo (x) :records ((:input '(2) :output-generator (function identity) :occur 2)))) (+ (foo 2) (foo 2) (foo 2) (foo 2)))) :type 'mocker-record-error) (ert-deftest mocker-let-stub-simple () (should (let ((mocker-mock-default-record-cls 'mocker-stub-record)) (eq t (mocker-let ((foo (x) :records ((:output t)))) (and (foo 1) (foo 42) (foo 666))))))) (ert-deftest mocker-let-stub-simple-explicit () (should (eq t (mocker-let ((foo (x) :records ((:record-cls mocker-stub-record :output t)))) (and (foo 1) (foo 42) (foo 666)))))) (ert-deftest mocker-let-stub-limited () (should-error (let ((mocker-mock-default-record-cls 'mocker-stub-record)) (mocker-let ((foo (x) :records ((:output t :max-occur 2)))) (and (foo 1) (foo 42) (foo 666)))) :type 'mocker-mock-error)) (ert-deftest mocker-let-multiple-calls-unordered () (should (eq 18 (mocker-let ((foo (x y z) :ordered nil ((:input '(1 2 3) :output 4) (:input '(4 5 6) :output 10))) (bar (x) ((:input '(42) :output 4)))) (+ (foo 4 5 6) (foo 1 2 3) (bar 42)))))) (ert-deftest mocker-passthrough-basic () (should (not (mocker-let ((ignore (x) :records ((:record-cls mocker-passthrough-record :input '(42))))) (ignore 42))))) (ert-deftest mocker-passthrough-mixed () (should (mocker-let ((ignore (x) :records ((:record-cls mocker-passthrough-record :input '(42)) (:input '(58) :output t)))) (or (ignore 42) (ignore 58))))) (ert-deftest mocker-passthrough-mixed-error () (should-error (mocker-let ((ignore (x) :records ((:record-cls mocker-passthrough-record :input '(42)) (:input '(58) :output t)))) (or (ignore 42) (ignore 42))) :type 'mocker-record-error)) (ert-deftest mocker-passthrough-multiple () (should (mocker-let ((ignore (x) ((:input-matcher (lambda (x) t) :output t :max-occur 2) (:record-cls mocker-passthrough-record :input '(42) :max-occur nil)))) (and (ignore 1) (ignore 2) (not (or (ignore 42) (ignore 42) (ignore 42) (ignore 42))))))) (ert-deftest mocker-inhibit-mock-not-consumed () (should-error (let ((mocker-inhibit t)) (mocker-let ((ignore (x) ((:input '(42) :output t)))) (ignore 42))) :type 'mocker-record-error)) (ert-deftest mocker-inhibit-mocking () (should (not (mocker-let ((ignore (x) ((:input '(42) :output t)))) (and (ignore 42) (let ((mocker-inhibit t)) (ignore 42))))))) (ert-deftest mocker-rest-args () (should (mocker-let ((f (a b &rest args) ((:input '(1 2 3 4) :output t)))) (f 1 2 3 4)))) (provide 'mocker-tests) ;;; mocker-tests.el ends here mocker.el-0.5.0/Makefile0000644000175000017500000000025013770565241014674 0ustar dogslegdogslegEMACS ?= emacs .PHONY: build test clean build: keg build test: keg exec $(EMACS) --batch -l test/mocker-test.el -f ert-run-tests-batch-and-exit clean: keg clean mocker.el-0.5.0/mocker.el0000644000175000017500000003344513770565241015052 0ustar dogslegdogsleg;;; mocker.el --- mocking framework for emacs ;; Copyright (C) 2011 Yann Hodique. ;; Author: Yann Hodique ;; Keywords: lisp, testing ;; Version: 0.5.0 ;; Package-Requires: ((emacs "25.1")) ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; ;;; Code: (require 'cl-lib) (require 'eieio) (defvar mocker-mock-default-record-cls 'mocker-record) (put 'mocker-mock-error 'error-conditions '(mocker-mock-error error)) (put 'mocker-mock-error 'error-message "Mocker mock error") (put 'mocker-record-error 'error-conditions '(mocker-record-error error)) (put 'mocker-record-error 'error-message "Mocker record error") (defun mocker--plist-remove (plist key) ;; courtesy of pjb (if (eq (car plist) key) (cdr (cdr plist)) (cons (car plist) (cons (cadr plist) (mocker--plist-remove (cddr plist) key))))) ;;; Mock object (defclass mocker-mock () ((function :initarg :function :type symbol) (orig-def :initarg :orig-def :initform nil) (argspec :initarg :argspec :initform nil :type list) (ordered :initarg :ordered :initform t) (records :initarg :records :initform nil :type list))) (cl-defmethod make-instance ((mock (subclass mocker-mock)) newname &rest args) (let* ((obj (cl-call-next-method)) (recs (oref obj :records)) (func (oref obj :function))) (oset obj :orig-def (when (fboundp func) (symbol-function func))) (oset obj :records nil) (mapc #'(lambda (r) (apply 'mocker-add-record obj r)) recs) obj)) (cl-defmethod mocker-add-record ((mock mocker-mock) &rest args) (object-add-to-list mock :records (let ((cls mocker-mock-default-record-cls) (tmp (plist-get args :record-cls))) (when tmp (setq cls tmp args (mocker-read-record cls (mocker--plist-remove args :record-cls)))) (apply 'make-instance cls :-mock mock :-sym (make-symbol "unique") args)) t)) (cl-defmethod mocker-fail-mock ((mock mocker-mock) args) (signal 'mocker-mock-error (list (format (concat "Unexpected call to mock `%s'" " with input `%s'") (oref mock :function) args)))) (defvar mocker-inhibit nil) (cl-defmethod mocker-run ((mock mocker-mock) &rest args) (if (not mocker-inhibit) (let* ((mocker-inhibit t) (rec (mocker-find-active-record mock args)) (ordered (oref mock :ordered))) (cond ((null rec) (mocker-fail-mock mock args)) ((or (not ordered) (mocker-test-record rec args)) (mocker-run-record rec args)) (t (mocker-fail-record rec args)))) (apply (oref mock :orig-def) args))) (cl-defmethod mocker-find-active-record ((mock mocker-mock) args) (let ((first-match (lambda (pred seq) (let ((x nil)) (while (and seq (not (setq x (funcall pred (pop seq)))))) x)))) (let* ((ordered (oref mock :ordered)) rec) (if ordered (setq rec (funcall first-match #'(lambda (r) (when (oref r :-active) (if (mocker-test-record r args) (progn (mocker-use-record r) r) (mocker-skip-record r args)))) (oref mock :records))) (setq rec (funcall first-match #'(lambda (r) (and (oref r :-active) (mocker-test-record r args) (progn (mocker-use-record r) r))) (oref mock :records)))) rec))) (cl-defmethod mocker-verify ((mock mocker-mock)) (mapc #'(lambda (r) (when (and (oref r :-active) (< (oref r :-occurrences) (oref r :min-occur))) (signal 'mocker-record-error (list (format (concat "Expected call to mock `%s'," " with input like %s," " was not run.") (oref mock :function) (mocker-get-record-expectations r)))))) (oref mock :records))) ;;; Mock record base object (defclass mocker-record-base () ((min-occur :initarg :min-occur :initform 1 :type number) (max-occur :initarg :max-occur :initform nil :type (or null number)) (-occur :initarg :occur :initform nil :type (or null number)) (-occurrences :initarg :-occurrences :initform 0 :type number :protection :protected) (-mock :initarg :-mock) (-active :initarg :-active :initform t :protection :protected) (-sym :initarg :-sym))) (cl-defmethod make-instance ((rec (subclass mocker-record-base)) newname &rest args) (let* ((obj (cl-call-next-method)) (occur (oref obj :occur))) (when occur (oset obj :min-occur (max (oref obj :min-occur) occur)) (oset obj :max-occur (if (oref obj :max-occur) (min (oref obj :max-occur) occur) occur))) obj)) (cl-defmethod mocker-read-record ((rec (subclass mocker-record-base)) spec) spec) (cl-defmethod mocker-use-record ((rec mocker-record-base)) (let ((max (oref rec :max-occur)) (n (1+ (oref rec :-occurrences)))) (oset rec :-occurrences n) (when (and (not (null max)) (= n max)) (oset rec :-active nil)))) (cl-defmethod mocker-skip-record ((rec mocker-record-base) args) (if (>= (oref rec :-occurrences) (oref rec :min-occur)) (oset rec :-active nil) (mocker-fail-record rec args))) (cl-defmethod mocker-test-record ((rec mocker-record-base) args) (error "not implemented in base class")) (cl-defmethod mocker-run-record ((rec mocker-record-base) args) (error "not implemented in base class")) (cl-defmethod mocker-get-record-expectations ((rec mocker-record-base))) (cl-defmethod mocker-fail-record ((rec mocker-record-base) args) (signal 'mocker-record-error (list (format (concat "Violated record while mocking `%s'." " Expected input like: %s, got: `%s' instead") (oref (oref rec :-mock) :function) (mocker-get-record-expectations rec) args)))) ;;; Mock input recognizer (defclass mocker-input-record (mocker-record-base) ((input :initarg :input :initform nil :type list) (input-matcher :initarg :input-matcher :initform nil))) (cl-defmethod make-instance ((rec (subclass mocker-input-record)) newname &rest args) (let* ((obj (cl-call-next-method))) (when (or (not (slot-boundp obj :max-occur)) (and (oref obj :max-occur) (< (oref obj :max-occur) (oref obj :min-occur)))) (oset obj :max-occur (oref obj :min-occur))) obj)) (cl-defmethod mocker-test-record ((rec mocker-input-record) args) (let ((matcher (oref rec :input-matcher)) (input (oref rec :input))) (cond (matcher (apply matcher args)) (t (equal input args))))) (cl-defmethod mocker-get-record-expectations ((rec mocker-input-record)) (format "`%s'" (or (oref rec :input-matcher) (oref rec :input)))) ;;; Mock record default object (defclass mocker-record (mocker-input-record) ((output :initarg :output :initform nil) (output-generator :initarg :output-generator :initform nil))) (cl-defmethod mocker-run-record ((rec mocker-record) args) (let ((generator (oref rec :output-generator)) (output (oref rec :output))) (cond (generator (apply generator args)) (t output)))) ;;; Mock simple stub object (defclass mocker-stub-record (mocker-record-base) ((output :initarg :output :initform nil))) (cl-defmethod make-instance ((rec (subclass mocker-stub-record)) newname &rest args) (let* ((obj (cl-call-next-method))) (unless (slot-boundp obj :min-occur) (oset obj :min-occur 0)) (unless (slot-boundp obj :max-occur) (oset obj :max-occur nil)) obj)) (cl-defmethod mocker-test-record ((rec mocker-stub-record) args) t) (cl-defmethod mocker-run-record ((rec mocker-stub-record) args) (oref rec :output)) (cl-defmethod mocker-get-record-expectations ((rec mocker-stub-record)) "anything") ;;; Mock passthrough record (defclass mocker-passthrough-record (mocker-input-record) ()) (cl-defmethod mocker-run-record ((rec mocker-passthrough-record) args) (let* ((mock (oref rec :-mock)) (def (oref mock :orig-def))) (when def (apply def args)))) ;;; Helpers (defun mocker-gen-mocks (mockspecs) "helper to generate mocks from the input of `mocker-let'" (mapcar #'(lambda (m) (let* ((func (car m)) (argspec (cadr m)) (rest (cddr m)) (sym (make-symbol (concat (symbol-name func) "--mock")))) (list sym (apply 'make-instance 'mocker-mock :function func :argspec argspec (let* ((order (if (plist-member rest :ordered) (prog1 (plist-get rest :ordered) (setq rest (mocker--plist-remove rest :ordered))) (oref-default 'mocker-mock :ordered)))) (list :ordered order))) (if (plist-member rest :records) (plist-get rest :records) (car rest))))) mockspecs)) ;;;###autoload (defmacro mocker-let (mockspecs &rest body) "Generate temporary bindings according to MOCKSPECS then eval BODY. The value of the last form in BODY is returned. Each element of MOCKSPECS is a list (FUNC ARGS [OPTIONS] RECORDS). FUNC is the name of the function to bind, whose original definition must accept arguments compatible with ARGS. OPTIONS can be :ordered nil if the records can be executed out of order (by default, order is enforced). RECORDS is a list ([:record-cls CLASS] ARG1 ARG2...). Each element of RECORDS will generate a record for the corresponding mock. By default, records are objects of the `mocker-record' class, but CLASS is used instead if specified. The rest of the arguments are used to construct the record object. They will be passed to method `mocker-read-record' for the used CLASS. This method must return a valid list of parameters for the CLASS constructor. This allows to implement specialized mini-languages for specific record classes. " (declare (indent 1) (debug t)) (let* ((mocks (mocker-gen-mocks mockspecs)) (vars (mapcar #'(lambda (m) `(,(car m) ,(cadr m))) mocks)) (specs (mapcar #'(lambda (m) (let* ((mock-sym (car m)) (mock (cadr m)) (func (oref mock :function)) (spec (oref mock :argspec)) (call (or (and (member '&rest spec) 'apply) 'funcall)) (args (cl-loop for el in spec if (or (not (symbolp el)) (not (equal (elt (symbol-name el) 0) ?&))) collect el))) (list `(symbol-function ',func) `(lambda ,spec (,call #'mocker-run ,mock-sym ,@args))))) mocks)) (inits (mapcar #'(lambda (m) (cons 'progn (mapcar #'(lambda (rec) `(mocker-add-record ,(car m) ,@rec)) (nth 2 m)))) mocks)) (verifs (mapcar #'(lambda (m) `(mocker-verify ,(car m))) mocks))) `(let (,@vars) ,@inits (prog1 ,(macroexpand `(cl-letf (,@specs) ,@body)) ,@verifs)))) (provide 'mocker) ;;; mocker.el ends here mocker.el-0.5.0/Keg0000644000175000017500000000016013770565241013665 0ustar dogslegdogsleg;; Keg (source gnu melpa) (package (mocker (recipe . (mocker :fetcher github :repo "sigma/mocker.el")))) mocker.el-0.5.0/README.markdown0000644000175000017500000001502313770565241015741 0ustar dogslegdogsleg[![Build Status](https://travis-ci.org/sigma/mocker.el.png?branch=master)](https://travis-ci.org/sigma/mocker.el) Mocker.el is a mocking framework for Emacs lisp. Its single entry point, `mocker-let` provides an `let` like interface to defining mock objects. Actually, `mocker-let` is a wrapper around `flet`, which can be seen as a way to manually generate mocks. ## Usage ### Basic usage Let's start with a simple example: ```lisp (mocker-let ((foo (x y z) ((:input '(1 2 3) :output 4) (:input '(4 5 6) :output 10))) (bar (x) ((:input '(42) :output 4)))) (+ (foo 1 2 3) (foo 4 5 6) (bar 42))) ``` Each mock is defined in a function-style, and is associated with a set of "records" that map expected inputs to desired outputs. ### Call order By default, the order of definition within a mock has to be respected by the wrapped code, so that in this situation it would be an error to observe `(foo 4 5 6)` before `(foo 1 2 3)`. ```lisp (mocker-let ((foo (x y z) ((:input '(1 2 3) :output 4) (:input '(4 5 6) :output 10))) (bar (x) ((:input '(42) :output 4)))) (+ (foo 4 5 6) (foo 1 2 3) (bar 42))) ``` In such a situation, you'll get a typed error with a message like ``` (mocker-record-error "Violated record while mocking `foo'. Expected input like: `(1 2 3)', got: `(4 5 6)' instead") ... ``` If order is not important, you can obtain the same effect as before by specifying it: ```lisp (mocker-let ((foo (x y z) :ordered nil ((:input '(1 2 3) :output 4) (:input '(4 5 6) :output 10))) (bar (x) ((:input '(42) :output 4)))) (+ (foo 4 5 6) (foo 1 2 3) (bar 42))) ``` ### Counting calls In many situations it can be pretty repetitive to list all the expected calls to a mock. In some, the count might even be a range rather than a fixed number. The `:min-occur` and `:max-occur` options allow to tune that. By default, they are both set to 1, so that exactly 1 call is expected. As a special case, setting `:max-occur` to nil will accept any number of calls. An `:occur` shorthand is also provided, to expect an exact number of calls. ```lisp (mocker-let ((foo (x) ((:input '(1) :output 1 :min-occur 1 :max-occur 3)))) (+ (foo 1) (foo 1))) ``` This example will accept between 1 and 3 calls to `(foo 1)`, and complain if that constraint is not fulfilled. Note the applied algorithm is greedy, so that as many calls as possible will count as part of the earliest constraints. ### Flexible input/output The examples above are fine, but they suppose input and output are just constant expressions. A useful addition is the ability to match arbitrary input and generate arbitrary output. To this end, the `:input-matcher` and `:output-generator` options can be used instead (actually think of `:input` and `:output` as convenience shortcuts for constant matcher/generator). ```lisp (mocker-let ((foo (x) :ordered nil ((:input-matcher 'oddp :output-generator 'identity :max-occur 2) (:input-matcher 'evenp :output 0)))) (+ (foo 1) (foo 2) (foo 3))) ``` Both `:input-matcher` and `:output-generator` values need to be functions (or function symbols) accepting the same arguments as the mocked function itself. ## Extensibility Each record definition actually builds a `mocker-record` object, that's responsible for checking the actual behavior. By providing alternative implementations of those records, one can adapt the mocking to special needs. ### Stubs As a quick proof of concept, an implementation of a stub is provided with the class `mocker-stub-record` which casualy ignores any input and always emits the same output: ```lisp (mocker-let ((foo (x) ((:record-cls 'mocker-stub-record :output 42)))) (foo 12345)) ``` ### Passthrough In some occasions, you might want to mock only some calls for a function, and let other calls invoke the real one. This can be achieved by using the `mocker-passthrough-record`. In the following example, the first call to `ignore` uses the real implementation, while the second one is mocked to return `t`: ```lisp (mocker-let ((ignore (x) :records ((:record-cls mocker-passthrough-record :input '(42)) (:input '(58) :output t)))) (or (ignore 42) (ignore 58))) ``` ### Provide your own Customized classes can be provided, that can even introduce a mini-language for describing the stub. This can be achieved by overloading `mocker-read-record` correctly. In case the customized record class is meant to be used in many tests, it might be more convenient to use a pattern like: ```lisp (let ((mocker-mock-default-record-cls 'mocker-stub-record)) (mocker-let ((foo (x) ((:output 42))) (bar (x y) ((:output 1)))) (+ (foo 12345) (bar 5 14)))) ``` Also note that `mocker-stub-record` set their `:min-occur` to 0 and `:max-occur` to nil, if not specified otherwise. ## Comparison to other mocking solutions * el-mock.el (http://www.emacswiki.org/emacs/EmacsLispMock) * el-mock.el uses a small DSL for recording behavior, which is great for conciseness. mocker.el instead uses regular lisp as much as possible, which is more flexible. * el-mock.el does not allow recording multiple behaviors (the same call will always return the same value). This makes it difficult to use in real situation, where different call sites for the same function might have to behave differently. ## Examples ```lisp ;;; automatically answer some `y-or-n-p' questions (mocker-let ((y-or-n-p (prompt) ((:input '("Really?") :output t) (:input '("I mean... for real?") :output nil)))) ...) ``` ```lisp ;;; blindly accept all `yes-or-no-p' questions (mocker-let ((yes-or-no-p (prompt) ((:record-cls mocker-stub-record :output t)))) ...) ``` ```lisp ;;; make `foo' generate the fibonacci suite, no matter how it's called (mocker-let ((foo (x) ((:input-matcher (lambda (x) t) :output-generator (lexical-let ((x 0) (y 1)) (lambda (any) (let ((z (+ x y))) (setq x y y z)))) :max-occur nil)))) ...) ```