使用 fiveam 测试包,我想测试由 main.lisp 脚本处理的 CLI 参数的解析。但是,解析参数的主函数不接受任何参数。因此,我想知道以下内容, 在我的案例中测试命令行解析的最佳实践是什么?
如果是这样,我应该如何实施?我如何测试 main-missing-arg-p() 中的 main 函数,就像我将使用“--interface wlo1”作为参数执行程序一样?
main.lisp,
(in-package :cl-user)
(defpackage kafar
(:use :cl)
(:export main))
(in-package :kafar)
;;; CLI Parser
(opts:define-opts
(:name :port
:description "port on the loopback interface on which the proxy server shall run. Recommended: 1080"
:short #\p
:long "port"
:required t
:arg-parser #'parse-integer
)
(:name :interface
:description "name of the interface kafar shall listen on"
:short #\i
:required t
:long "interface"
:arg-parser #'identity)
(:name :help
:description "print this help text"
:short #\h
:long "help"))
(defun unknown-option (condition)
...)
(defun missing-arg (condition)
...)
(defmacro when-option ((options opt) &body body)
`...)
(defun main ()
(multiple-value-bind (options free-args)
(handler-case
(handler-bind ((opts:unknown-option #'unknown-option))
(opts:get-opts))
(opts:missing-arg (condition)
(format t "fatal: option ~s needs an argument!~%"
(opts:option condition))
(uiop:quit))
(opts:arg-parser-failed (condition)
(format t "fatal: cannot parse ~s as argument of ~s.~%"
(opts:raw-arg condition)
(opts:option condition))
(uiop:quit))
...
(when-option (options :port)
(let ((interfacei (getf options :interface))
(porti (getf options :port)))
(kafar/proxy:proxy-server porti interfacei))
(uiop:quit)
)))
test-main.lisp:
(in-package :kafar/tests)
;;; Testing Proxy server
(def-suite* parse-suite
:description "Test parsing of CLI arguments"
:in kafar-suite)
;; test the stdout without arguments
(test main-help
; call main function without aguments
(print (unix-opts:argv)) ; => ("sbcl")
(let ((output (with-output-to-string (*error-output*) (kafar:main))))
(is (string= "warning: missing required options: \"--port\", \"--interface\"" output))))
;; test stderr missing port argument
(test main-missing-arg-p
; define interface argument, not define port argument
;Potentially modify (unix-opts:argv) (sb-ext:*posix-argv*) such that they would be equal to ("sbcl" "--interface 80")
; call main function (handling CLI argument parsing)
)
我在shell中执行如下测试,
sbcl --non-interactive --eval "(progn (ql:quickload '(fiveam usocket nibbles unix-opts trivial-coverage)) (asdf:load-asd (merge-pathnames \"kafar.asd\" (uiop:getcwd))) (asdf:test-system 'kafar/coverage))"
您可以使用不起眼的对象模式 (https://martinfowler.com/bliki/HumbleObject.html),这实际上涉及将接收到的 cmd 行参数传递给您能够测试的另一个函数。您可以决定是按原样传递参数,还是定义一个契约来转换参数。无论如何,了解“unix-opts”将如何传递参数是很重要的。
使用您的
opts:define-opts
子句,您可以通过将参数列在列表中来测试参数get-opts
:
CL-USER> (opts:get-opts '("--interface" "wlo1"))
; Evaluation aborted on #<UNIX-OPTS:MISSING-REQUIRED-OPTION {10024637C3}>.
所以我会让
main
分成run
和entry
函数,第一个接受参数列表,第二个是程序的入口点,它调用run
和(opts:argv)
.