You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lisp中破坏性函数单元测试的常量修改问题及解决建议

处理Lisp破坏性函数单元测试中常量列表修改的方案

在将非破坏性函数改造为破坏性函数时,单元测试里因Lisp将源码中的引用列表视为常量,修改这类列表会导致测试用例间互相干扰。以下是几种比直接嵌套list更优的处理方案:

1. 用copy-tree生成独立的可变结构

copy-tree会递归复制整个列表结构,生成完全独立的可变对象,既保留了字面量写法的可读性,又彻底避免了常量共享问题:

(define-test suite-1
  (let ((start (copy-tree '((1 2) (3 4) (5 6))))
        (end '((7 1 2) (3 4) (5 6))))
    (is #'equal end (push 7 (car start))))
  (let ((start (copy-tree '((1 2) (3 4) (5 6))))
        (end '((8 1 2) (3 4) (5 6))))
    (is #'equal end (push 8 (car start)))))

每次测试都会基于字面量生成全新的可修改列表,不会出现前一次测试修改常量影响后一次的情况。

2. 封装测试数据生成函数

如果测试用的结构重复出现,写一个专用函数来生成数据,既能提升可读性,又能保证每次调用都返回新的可变列表:

(defun make-test-start-data ()
  (list (list 1 2) (list 3 4) (list 5 6)))

(define-test suite-1
  (let ((start (make-test-start-data))
        (end '((7 1 2) (3 4) (5 6))))
    (is #'equal end (push 7 (car start))))
  (let ((start (make-test-start-data))
        (end '((8 1 2) (3 4) (5 6))))
    (is #'equal end (push 8 (car start)))))

函数名make-test-start-data清晰表达了数据用途,比嵌套的list写法更易读,同时隔离了数据生成逻辑,方便后续修改测试结构。

3. 利用Parachute的测试夹具初始化干净数据

Parachute支持测试夹具,可以在每个子测试运行前自动初始化未被修改的测试数据,让测试代码更整洁:

(define-test suite-1
  :before (lambda ()
            (setf *test-start-data* (list (list 1 2) (list 3 4) (list 5 6))))
  :tests ((test-push-7
           (let ((end '((7 1 2) (3 4) (5 6))))
             (is #'equal end (push 7 (car *test-start-data*)))))
          (test-push-8
           (let ((end '((8 1 2) (3 4) (5 6))))
             (is #'equal end (push 8 (car *test-start-data*)))))))

通过:before钩子,每个子测试都会拿到全新的测试数据,无需在每个let里重复写数据生成代码,适合测试用例较多的场景。

这些方案的核心思路都是确保每个测试用例使用独立的、可修改的列表结构,彻底避免共享常量带来的意外修改问题。

内容的提问来源于stack exchange,提问作者owagh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 16:55:15