Common Lisp有一个模拟/存根框架吗?

时间:2010-11-01 11:11:34

标签: unit-testing tdd mocking lisp common-lisp

是否存在Common Lisp的模拟/存根框架?

EmacsLispMock看起来很棒,但它是一个Emacs lisp框架,我正在寻找可以使用Common Lisp的东西。

有什么建议吗?

7 个答案:

答案 0 :(得分:4)

以下内容应该是您正在寻找的内容

(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))

宏只是保存符号当前指向的函数,并将其替换为提供的存根实现。在块结束时,该函数将恢复为原始值。

添加对身体非本地出口的保护可能是有意义的。

另请注意,如果编译器已内联函数调用,那么显然更改函数定义将不起作用。 CL有一个特殊的NOTINLINE声明,可用于防止此问题。

答案 1 :(得分:2)

正如Rainer所指出的那样,文件编译器有时会内联函数,这意味着更改函数定义不会对函数内联的地方产生任何影响。

更改函数名的定义也不会取代函数作为文字对象的使用,例如,如果你在某个变量中保存了''my-stubbed-function。

但是,在某些lisp实现中,您可以定义函数包装器或使用建议来实现此目的:例如,Allegro CL具有fwrappers。在SBCL中,您可以将TRACE与非标准:break函数和自定义* invoke-debugger-hook *一起使用,我确信其他lisp实现也会有类似的东西。

我认为没有人将这些存根方法打包到库中。你能成为第一个! (它会很棒。我喜欢用这样的东西。)

答案 2 :(得分:1)

您不需要CL中的模拟/存根框架。

只需使用方法ovverides创建从类类派生的新CLOS,即可实现存根/模拟,并且已完成。

至于存根,为什么不重新定义功能?

答案 3 :(得分:1)

这不是最简单的方法吗?

> (defun b () 'original)
B
> (setf f #'b)
#<Compiled-function B #xC2C1546>
> (defun a () (funcall f))
A
> (a)
ORIGINAL
> (setf f #'(lambda () 'stub))
#<Anonymous Function #xC2D990E>
> (a)
STUB
> (setf f #'b)
#<Compiled-function B #xC2C1546>
> (a)
ORIGINAL

答案 4 :(得分:1)

我用一个非常类似于@ 6502答案(with-mocked-functions)的宏编写了一个库,但更为一般。它还提供了with-added-methods,它允许您为有限的动态范围编写模拟方法。您可以在此处找到它:https://github.com/bytecurry/bytecurry.mocks

答案 5 :(得分:0)

您可以尝试在宏中包装函数重定义

(defmacro with-fun (origfn mockfn &body body)
  `(let ((it ,origfn))
      (setf ,origfn ,mockfn)
     ,@body
      (setf ,origfn ,it)))

这只是一个想法,你将不得不实现这样的宏。 你可以谷歌进行profile实现,确切地说,将一个函数替换为另一个函数并添加分析信息。你可以从那里借一些想法。

答案 6 :(得分:0)

几年后,有。我们在Quicklisp中都有cl-mockmockingbird

(ql:quickload :mockingbird)

这个也允许检查一个函数是否被调用,如果是这样,有多少次以及哪些参数可以存储单个方法。

相关问题