工程化的最后两块拼图:测试(Test 标准库,零配置)与文档(Documenter,从 docstring 直出网站)。再加上会查 Discourse 与 juliahub,你的问题解决能力就闭环了。
在项目里建 test/runtests.jl:
using Test using MyTools # 你自己的包 @testset "几何计算" begin @test area(Circle(1.0)) ≈ π @test perimeter(Circle(2.0)) ≈ 4π @test_throws DomainError area(Circle(-1.0)) end
] test 一条命令运行全部测试。三个常用断言:
| 断言 | 用途 |
|---|---|
@test 表达式 |
布尔判断 |
@test a ≈ b |
浮点近似比较(波浪号是≈) |
@test_throws 错误类型 表达式 |
断言会抛指定异常 |
数值代码特别要用 ≈ 而不是 ==——浮点世界里精确相等几乎总是错的断言。
每个公开函数写 docstring,是 Julia 的文档习惯:
""" area(c::Circle) 计算圆的面积。 # 参数 - `c::Circle`: 圆对象 # 示例 area(Circle(2.0)) # 结果约 12.566 """ area(c::Circle) = π * c.r^2
在 REPL 里 ?area 立即可查。配 Documenter 后,这些 docstring 能生成完整文档网站,# 示例 里的代码块还会被真实执行验证——文档骗不了人。
| 场景 | 去处 |
|---|---|
| 语言与包的使用问题 | Julia 官方 Discourse 论坛(英文)与中文社区的 Zulip |
| 疑似 bug | 先在包的仓库 issue 区搜索,再最小复现后提 issue |
| 找包 | JuliaHub 包检索与 Julia 官方包注册表页面 |
| 快速判断包健康度 | 看最近提交时间、issue 响应、是否通过 CI |
⚠️ 常见坑:提问题贴一段"我这里跑不起来"的描述,没人能帮你。最小可复现示例(十几行、含版本号与完整报错栈)是社区通行的入场券,也是你自己排查时最有效的手段——一半的问题在构造最小示例的过程中就自己解决了。
💡 关键直觉:判断一个 Julia 包能不能用在生产,看三件事——测试覆盖、维护频率、docstring 质量。三者皆差的包,再诱人的功能也别依赖。
把 8.3 的两块拼图装回第 3 章的项目。背景:DataClean 包已经有 clean! 与 describe_col,现在让它达到"可交付"标准。第一步,补测试目录。在包根下建 test/runtests.jl:
using Test using DataClean @testset "describe_col" begin v = [1.0, 2.0, 3.0] r = describe_col(v) @test r.最小 == 1.0 @test r.均值 ≈ 2.0 @test_throws ArgumentError describe_col(Float64[]) # 空数组应报错 end
注意两点:具名元组的字段直接用点号断言,测试可读到一眼明意;空数组是数值函数最常被遗忘的边界,显式断言"应当抛错"把契约写死。第二步,跑 ] test DataClean,绿灯后给公开函数补 docstring(格式照本节开头的模板,示例代码必须真实可跑——Documenter 会执行它们,示例过期会直接构建失败,这个机制让文档永不过期)。第三步,把测试挂进版本管理,每次提交前跑一遍。结果解读:三步之后这个包有了"行为契约"(测试)与"使用说明"(文档),别人接手或半年后的你自己回来,都不用读源码猜语义。变式:测试里加一个随机性质测试——任意随机向量满足 describe_col(v).最小 ≤ describe_col(v).均值 ≤ describe_col(v).最大,这类"恒真性质"测试一次覆盖无数具体用例,是数值代码测试的高性价比写法。
按顺序过五条,多数问题不用发帖:查 docstring(?函数名)确认参数含义没记错;查 methods(函数) 确认自己调的是以为的那个方法;在最小脚本里复现,排除环境与数据因素;搜索包的 issue 区,常见坑几乎都有前人踩过;对比一次干净会话(重启 REPL、只 load 必要的包)的行为。五条走完仍无解,你手头正好已经握着一个高质量的最小复现示例,发出去就能得到有效回答。这套流程的本质是把"求助"变成"提交一份 bug 报告",无论对面是人还是将来的自己,都省掉所有来回追问。
文档与测试之外,可维护性还差最后一块:追踪"为什么这么改"。三个轻量实践。第一,遵循语义化版本号:破坏性接口变更升主版本、加功能升次版本、修 bug 升补丁版本,下游使用者据此决定敢不敢升级你的包。第二,变更日志:包根下维护一个记事文件,每个版本列新增、修复、破坏性变更三段,写给自己半年后的复盘用。第三,测试先行修 bug:每个报告的 bug 先写一个能复现它的失败测试,再修到绿灯——测试既证明了 bug 真实存在,又永久防止它回来。这三条都不需要额外工具,靠的只是纪律,却恰好是"个人脚本"与"可维护软件"的分界线。
从求助者到贡献者的路径其实很短,按顺序走:先用好搜索(Discourse 与 issue 区的历史帖子覆盖了绝大多数常见问题);提问时附上合格的最小复现;当发现自己踩的坑没有现成答案、自己解决后,把答案回帖——这一步就已经在给社区补文档;更进一步,给常用包修文档错字、补失败测试,是最容易被合并的首次贡献;最后才是功能代码贡献。Julia 社区的规模决定了每个认真参与者的可见度都很高,一条高质量的 issue 往往当天就有维护者回应,这种反馈密度在大型语言社区里反而少见。
@testset + ≈ + @test_throws 三招覆盖数值代码测试;?名 即查,Documenter 出网站还会验证示例;