为何test.check测试自定义index-of时使用小于设定的输入?
问题分析与解决
为什么会出现短输入的失败案例?
test.check 在发现失败案例后,会自动**缩小(shrink)**输入,找到能触发失败的最小可能值。你看到的"0bU"或"240"并不是直接生成的短输入,而是从你设定的100+字符的原始失败输入中缩减而来的最小复现案例,这是test.check的核心特性之一,帮助快速定位问题根源。
代码中的两个关键问题
1. 属性测试的绑定语法错误
你的prop/for-all绑定存在两处问题:
[ s1 (gen-alphanumeric 100 200) begin gen/nat 80 ]
gen/nat不接受参数,你想限制begin范围的写法无效;begin可能生成超出s1有效范围的值:当begin +10 > (count s1)时,subs会返回短于10字符的字符串(甚至空字符串),导致索引对比出现不一致。
正确做法是使用依赖生成器,确保begin的取值始终满足begin +10 ≤ (count s1):
(defspec indexof-functionality-test (prop/for-all [s1 (gen-alphanumeric 100 200) begin (gen/bind (gen/return s1) (fn [s] (gen/choose 0 (- (count s) 10))))] (let [end (+ begin 10) s2 (subs s1 begin end)] (= (str/index-of s1 s2) (cp-str/indexof s1 s2)))))
2. 自定义indexof函数与标准库行为不一致
当搜索字符串为空时,str/index-of返回0,但你的函数返回nil,这会直接导致测试失败。修正空字符串的处理逻辑:
(defn indexof "given two strings, returns the index where sb begins in sa. Nil if not found" ([sa sb] (indexof sa sb 0)) ([sa sb i] (let [sa-len (count sa) sb-len (count sb)] (cond (zero? sb-len) 0 ; 匹配str/index-of的空字符串行为 (>= i sa-len) nil (> (+ i sb-len) sa-len) nil (= (subs sa i (+ i sb-len)) sb) i :else (indexof sa sb (inc i))))))
为什么不同电脑上结果不同?
test.check 使用随机种子生成测试用例。失败的那次运行使用了特定种子1676231835751,刚好生成了触发问题的begin值;而另一台电脑的测试用例没有命中这个场景。你可以在另一台电脑上用该种子复现失败:
(prop/run-tests indexof-functionality-test :seed 1676231835751)
内容的提问来源于stack exchange,提问作者triplej
相关产品推荐
相关产品推荐

