在每次测试运行之前清除Cursive REPL状态

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在每次测试运行之前清除Cursive REPL状态相关的知识,希望对你有一定的参考价值。

我是Cursive和Clojure的新手,并且在获得一个像样的TDD工作流程时遇到了一些困难。

我的问题是后续的测试运行取决于REPL中的状态。例如,假设您有以下代码。

(def sayHello "hello")

(deftest test-repl-state
  (testing "testing state in the repl"
      (is (= "hello" sayHello)))) 

如果使用“工具 - > REPL->在REPL中以当前ns运行测试”来运行它,它将通过。

如果你然后像这样重构代码

(def getGreeting "hello")

(deftest test-repl-state
  (testing "testing state in the repl"
      (is (= "hello" sayHello))))  

如果使用“工具 - > REPL->在REPL中以当前ns运行测试”来运行它,它仍将通过(因为sayHello的def仍然存在于repl中)。但是,测试应该失败,因为代码当前处于失败状态(sayHello未在代码中的任何位置定义)。

我已尝试在REPL窗口中切换“本地将被清除”按钮,但这似乎无法解决问题。

如果有一种方法可以在REPL之外运行测试(或者在每次测试运行的新REPL中运行),我可以将其作为解决方案。

我想要的是被测源代码与测试结果之间存在1对1的对应关系。

在此先感谢您的帮助。

答案

是的,拥有旧的defs很烦人。我甚至不打造测试(哎呀),但这在正常发展过程中咬了我。如果我创建一个函数,然后重命名它,然后更改它,然后意外地引用第一个函数名称,我得到奇怪的结果,因为它指的是旧函数。我仍然在寻找一个很好的解决方法,不涉及杀死和重新启动REPL。

但是,对于您的特定情况,有一些简单,差的解决方法:

  • 打开IntelliJ的终端(窗口左下角的按钮)并运行lein test。这将执行所有项目的测试并报告结果。
  • 与上述类似,您可以在IntelliJ之外打开项目目录中的命令窗口并运行lein test,它将运行所有找到的测试。

您还可以使用lein test <ns here>(例如lein test beings-retry.core-test)指定要测试的命名空间,或使用:only指定命名空间中的特定测试(例如lein test :only beings-retry.core-test/a-test;其中a-testdeftest)。不幸的是,这不会发生在REPL中,所以它打破了工作流程。


如上所述,我所知道的唯一基于REPL的解决方法就是杀死REPL:

  • “停止REPL”(Ctrl + F2)
  • “重新连接”(Ctrl + F5)。

当然,这很慢,如果你经常这样做,这是一个糟糕的解决方案。我很想知道是否还有其他人有更好的解决方案。

另一答案

你可以使用Built-in test narrowing (test selector) lein插件的test-refresh功能。它允许每次保存文件时只测试那些用^:test-refresh/focus meta标记的测试。

另一答案

这种问题的通常解决方案是stuartsierra/componenttolitius/mount

完整的描述在这里是不合适的,但一般的想法是让一些系统以允许干净地重新加载应用程序状态的方式管理状态。这有助于在交互式处理正在运行的系统时保持接近源文件中保存的代码。

另一答案

感谢大家的建议。我发布了自己对这个问题的答案,因为我找到了一条对我有用的方法,而且我不确定上述任何一个都是我想要的。

我得出的结论是,clojure REPL虽然很有用,但并不是我要进行测试的地方。这基本上归结为在运行命令以清除每个测试运行之间的repl(如tools.namespace refresh中非常有用的https://github.com/clojure/tools.namespace函数)或不在REPL中运行测试之间进行选择。

我选择了后一种选择因为。

  1. 这是一个较少的步骤(重新加载并不总是完美的)
  2. CI测试不在REPL中运行,因此直接在dev中运行它们离CI环境更近了一步。
  3. 生产中的代码也不在REPL中运行,因此在repl之外运行测试更接近生产代码运行的方式。

在IntelliJ中配置运行配置以在应用程序中运行单个测试或所有测试作为普通的clojure应用程序实际上是非常简单的事情。如果您愿意,您甚至可以同时运行REPL,并根据需要使用它。在某种程度上,工具如此严重地倾向于在REPL中运行物品这一事实使我无视这一选择。

run tests within cursive

我对Clojure很缺乏经验,也是一个以TDD方式设置的顽固的老山羊,但至少其他一些人同意我关于这个https://github.com/cursive-ide/cursive/issues/247

此外,如果有人有兴趣,有一个很好的谈论如何REPL坚持国家,以及这如何导致各种奇怪的行为在这里https://youtu.be/-RaFcpNiYCo。事实证明,我所看到的重新定义功能的问题只是冰山一角。

另一答案

一个可能有用的选项,特别是如果你捆绑几个断言,或者重复测试的是let。名称 - 值绑定具有已知范围,可以避免重新键入大量内容。

这是一个例子:

(deftest my-bundled-and-scoped-test
   (let [TDD    "My expected result"
         helper (some-function :data)]
     (testing "TDD-1: Testing state in the repl"
       (is (= TDD "MY expected result")))
     (testing "TDD-2: Reusing state in the repl"
       (is (= TDD helper)))))

一旦my-bundled-and-scoped测试完成执行,您将不再处于let绑定中。另外一个好处是some-function的结果也是可重用的,这对于测试同一函数/输入对的多个断言或属性很方便。

关于这个问题,我还建议使用Leiningen来运行测试,因为有很多插件可以帮助您更有效地测试。我要结账test-refreshspecljcloverage

以上是关于在每次测试运行之前清除Cursive REPL状态的主要内容,如果未能解决你的问题,请参考以下文章

如何在每次测试之前重置或清除 ApolloProvider 的 useQuery 模拟?

重新运行测试时出现 NullPointerException

如何在 Android Studio 中每次运行之前自动清除 logcat 输出?

如何在aws设备场中为android espresso每次测试运行后清除设备数据

在 Spring Boot 中,如何在每次测试之前重置指标注册表?

为啥每次单元测试后我的数据库都没有被清除?