V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
moonbitlang
V2EX  ›  编程

推荐一个好用的测试工具,值得体验!

  •  
  •   moonbitlang · 14 天前 · 364 次点击

    在软件开发领域中,测试是确保质量与可靠性的必要环节。俗话说得好“工欲善其事,必先利其器”,测试工具越简单、用户友好度越高,开发者编写测试的意愿度就越高。

    为了满足大家的测试需求,MoonBit 标准库最近引入了 inspect 函数,我们也称之为 expect 测试,它可以帮助程序员快速编写测试。

    相比于 OCaml 和 Rust ,MoonBit 提供了更加简洁高效的测试体验。我们的测试工具不仅操作简便,而且无需任何外部依赖,支持开箱即用,简化了测试流程。

    这里简单介绍一下 MoonBit

    MoonBit 是国内首个工业级编程语言及其配套工具链( https://www.moonbitlang.cn/) 是由粤港澳大湾区数字经济研究院(福田)基础软件中心打造的 AI 原生的编程语言以及开发者平台。MoonBit 自 2022 年 10 月推出以来,通过创新框架在程序语言界形成后发优势,已在编译速度、运行速度和程序体积上取得了显著的优势。MoonBit 平台的出现不仅仅作为一种编程语言,更提供一个完整的开发工具链,包括 IDE 、编译器、构建系统、包管理器等。

    接下来,让我们进一步了解 inspect 函数的使用。

    忽略掉与位置相关的参数后,inspect 函数签名为:

    pub fn inspect(obj : Show, ~content: String = "")
    

    这里 obj 是任意一个实现 Show 接口的对象,~content 是一个可选参数,表示我们所期望的 obj 转化为字符串后的内容。听起来有点绕?让我们先来看看 inspect 的基本用法:

    01 基本用法

    首先,让我们使用 moon new hello 创建一个新项目

    此时项目的目录结构如下:

    .
    ├── README.md
    ├── lib
    │   ├── hello.mbt
    │   ├── hello_test.mbt
    │   └── moon.pkg.json
    ├── main
    │   ├── main.mbt
    │   └── moon.pkg.json
    ├── moon.mod.json
    

    打开 lib/hello_test.mbt ,将内容替换为:

    fn matrix(c: Char, n: Int) -> String {
      let mut m = ""
      for i = 0; i < n; i = i + 1 {
        for j = 0; j < n; j = j + 1 {
          m = m + c.to_string()
        }
        m += "\n"
      }
      m
    }
    

    这里,matrix 函数接受一个字符 c 和整数 n 作为参数,生成一个 n * n 大小的字符矩阵。

    接下来,添加如下内容:

    test {
      inspect(matrix('🤣', 3))?
    }
    

    打开终端,执行 moon test 命令可以观察到类似如下输出:

    Diff:
    ----
    🤣🤣🤣
    🤣🤣🤣
    🤣🤣🤣
    
    ----
    

    这里的输出展示了 matrix 函数的实际输出和 ~content 参数的差异,执行 moon test -u 或者 moon test --update 可以观察到 lib/hello_test.mbt 文件中的测试块被自动更新成:

    test {
      inspect(matrix('🤣', 3), ~content=
        #|🤣🤣🤣
        #|🤣🤣🤣
        #|🤣🤣🤣
        #|
      )?
    }
    

    让我们再把 n 改成 4 ,然后执行 moon test -u 可以观察到测试块被自动更新成:

    test {
      inspect(matrix('🤣', 4), ~content=
        #|🤣🤣🤣🤣
        #|🤣🤣🤣🤣
        #|🤣🤣🤣🤣
        #|🤣🤣🤣🤣
        #|
      )?
    }
    

    GIF 图 3.gif

    02 稍微复杂的例子

    一般来说,写完一个函数后,我们需要为其编写一些单元测试。最简单的是断言测试,MoonBit 标准库中提供了 @assertion.assert_eq 函数,用于判断两个值是否相等。对于上述例子来说测试编写相对容易,因为我们可以轻易预测输出结果是什么。

    接下来让我们看一个稍微复杂一点的例子:如何测试计算斐波那契数列第 n 项的函数。

    首先在 lib 目录下新建一个 fib.mbt 的文件,然后粘贴如下内容:

    fn fib(n : Int) -> Int {
      match n {
        0 => 0
        1 => 1
        _ => fib(n - 1) + fib(n - 2)
      }
    }
    

    我们需要一些测试来验证我们的实现是否正确。如果用断言测试,那么编写测试的流程是什么呢?假设我们想测试输入为 10 时 fib 的结果,我们会写下类似如下的代码:

    test {
      @assertion.assert_eq(fib(10), ???)?
    }
    

    当我们写下这个测试的时候,会立即遇到一个问题,我们并不知道 assert_eq 中右侧的期望值应该是什么。我们可以自己在纸上计算,或者找一份斐波那契数列参考列表、或者执行我们自己实现的 fib 函数。总之,不论用什么方法,我们需要得到 fib(10) 的期望值是 55 ,才能完成一个测试用例的编写。

    此时 lib/fib.mbt 文件内容应为:

    fn fib(n : Int) -> Int {
      match n {
        0 => 0
        1 => 1
        _ => fib(n - 1) + fib(n - 2)
      }
    }
    
    test {
      @assertion.assert_eq(fib(10), 55)?
    }
    

    然后执行 moon test 可以观察到如下输出:

    Total tests: 2, passed: 2, failed: 0.
    

    大家可以感受到,这里的反馈周期是很长的。通常来说,用这种方法编写测试并不是很愉悦。

    对于 fib 这个例子我们可以相对容易找到一个正确的值作为参考。然而,大多数情况下,我们想测试的函数并没有其他“真值表”作为参考。我们需要做的是给这个函数提供输入,然后观察其输出是否符合我们的期望。这种模式比较常见,但其他工具链很少提供支持。因此,我们的 MoonBit 工具链提供了对这一测试模式的一等支持。通过使用 inspect 函数,我们只需要提供输入,而不需要提供期望值。

    接下来,让我们用 inspect 函数编写输入分别为 8 ,9 ,10 时的测试用例,此时 lib/fib.mbt 文件的内容如下:

    fn fib(n : Int) -> Int {
      match n {
        0 => 0
        1 => 1
        _ => fib(n - 1) + fib(n - 2)
      }
    }
    
    test {
      @assertion.assert_eq(fib(10), 55)?
    }
    
    test {
      inspect(fib(8))?
    }
    
    test {
      inspect(fib(9))?
    }
    
    test {
      inspect(fib(10))?
    }
    

    通过执行:

    moon test
    

    可以观察到实际输出与 inspect 函数中 ~content 的差异:

    $ moon test
    test username/hello/lib/fib.mbt::0 failed
    expect test failed at path/to/lib/fib.mbt:10:3-10:18
    Diff:
    ----
    21
    ----
    
    test username/hello/lib/fib.mbt::1 failed
    expect test failed at path/to/lib/fib.mbt:14:3-14:18
    Diff:
    ----
    34
    ----
    
    test username/hello/lib/fib.mbt::2 failed
    expect test failed at path/to/lib/fib.mbt:18:3-18:19
    Diff:
    ----
    55
    ----
    

    接下来,我们的工作变成了检查这个输出是否正确,如果确信这些输出是正确的,通过执行 moon test -u ,在 lib/fib.mbt 文件中对应的测试块会被自动更新成:

    test {
      inspect(fib(8), ~content="21")?
    }
    
    test {
      inspect(fib(9), ~content="34")?
    }
    
    test {
      inspect(fib(10), ~content="55")?
    }
    

    通过这种编写测试然后立即获取反馈的模式,能够极大提升编写测试的愉悦感。

    接下来,让我们来看一个修改函数行为导致输出变化的例子。

    例如,我们现在想让 fib 的第一项从 1 而不是从 0 开始,首先我们将 fib 函数中的 0 => 0 修改为 0 => 1:

    fn fib(n : Int) -> Int {
      match n {
    

    GIF2.gif 然后执行 moon test 可以看到 expect test 自动为我们展示了前后差异:

    $ moon test
    test username/hello/lib/fib.mbt::0 failed: FAILED:path/to/lib/fib.mbt:10:3-10:36 `89 == 55`
    test username/hello/lib/fib.mbt::1 failed
    expect test failed at path/to/lib/fib.mbt:14:3-14:33
    Diff:
    ----
    2134
    ----
    
    test username/hello/lib/fib.mbt::2 failed
    expect test failed at path/to/lib/fib.mbt:18:3-18:33
    Diff:
    ----
    3455
    ----
    
    test username/hello/lib/fib.mbt::3 failed
    expect test failed at path/to/lib/fib.mbt:22:3-22:34
    Diff:
    ----
    5589
    ----
    
    Total tests: 5, passed: 1, failed: 4.
    

    这里的输出结果发生了位移,符合期望,我们大概率可以确定输出是对的。于是通过执行 moon test -u 自动更新测试结果。

    1b41eb0b-cd5f-4e3d-a52b-8100090e99ed.gif

    等等!为什么自动更新后,还有一个测试用例失败了呢?

    Total tests: 5, passed: 4, failed: 1.
    

    这是因为我们忘记了修改断言测试,与 expect 测试不同,断言测试并不会自动更新。我们需要手动将断言测试对应的测试块修改为:

    test {
      @assertion.assert_eq(fib(10), 89)?
    }
    

    从这个例子也能看出来,expect 测试是可以与断言测试协同工作的。

    重新执行 moon test ,可以看到全部测试都通过了。

    Total tests: 5, passed: 5, failed: 0.
    

    想象一下,如果我们之前有数百个断言测试,修改起来将会非常麻烦。通过使用 expect 测试,可以让我们从枯燥的更新工作中解脱出来。

    以上,我们通过两个例子展示了它们如何通过编写测试并获得即时反馈,显著提升测试效率的能力。

    我们鼓励你尝试使用 MoonBit 的 expect 测试,体验其在实际应用中的便利和高效。

    推荐阅读:

    https://blog.janestreet.com/the-joy-of-expect-tests/

    你还可以了解更多内容:

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2172 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 16:07 · PVG 00:07 · LAX 09:07 · JFK 12:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.