Search code examples
unit-testingtddmockinglispcommon-lisp

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?


Solution

  • 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.