Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a mocking/stubbing framework for Common Lisp?

Is there a mocking/stubbing framework for Common Lisp?

EmacsLispMock looks great, but it is an Emacs lisp framework, and I'm looking for something to use from Common Lisp.

Any suggestions?

like image 479
Erik Öjebo Avatar asked Nov 01 '10 11:11

Erik Öjebo


2 Answers

A few years later, there is. We have cl-mock and mockingbird both in Quicklisp.

(ql:quickload :mockingbird)

This one also allows to check if a function was called, if so how many times and with which arguments and it's possible to stub individual methods.

like image 86
Ehvince Avatar answered Sep 20 '22 18:09

Ehvince


The following should do what you're looking for

(defmacro with-replaced-function (fdef &rest body)
  (let ((oldf (gensym))
        (result (gensym))
        (name (car fdef))
        (args (cadr fdef))
        (rbody (cddr fdef)))
    `(let ((,oldf (symbol-function ',name)))
       (setf (symbol-function ',name) (lambda ,args ,@rbody))
       (let ((,result (progn ,@body)))
         (setf (symbol-function ',name) ,oldf)
         ,result))))

(defmacro show (x)
  `(format t "~a --> ~a~%"
           ',x ,x))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun foo (x y) (+ x y))

(defun bar (x) (foo x (* x 2)))

(show (bar 42))

(show (with-replaced-function (foo (x y) (* x y))
                              (bar 42)))

(show (bar 42))

The macro simply saves the function being currently pointed by the symbol and replaces it with the provided stub implementation. At the end of the block the function is restored to the original value.

It would probably make sense to add a protection against non-local exits from the body.

Note also that obviously changing a function definition is not going to work if function calls have been inlined by a compiler. CL has a special NOTINLINE declaration that can be used to prevent this problem.

like image 40
6502 Avatar answered Sep 20 '22 18:09

6502