每次测试运行前清除草书REPL状态

时间:2018-11-12 17:58:18

标签: testing clojure cursive

我通常对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的定义仍然存在于repl中)。但是,测试应该失败,因为代码当前处于失败状态(在代码中的任何地方都未定义sayHello

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

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

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

在此先感谢您的帮助。

5 个答案:

答案 0 :(得分:0)

是的,拥有旧的def很烦人。我什至通常不创建测试(抱怨),但这在正常开发过程中使我感到痛苦。如果我创建一个函数,然后对其重命名,然后对其进行更改,然后不小心引用第一个函数名称,由于引用的是旧函数,我会得到奇怪的结果。我仍在寻找解决此问题的好方法,该方法不涉及终止和重新启动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)。

当然,这很慢,而且如果您不断这样做,这将是一个糟糕的解决方案。我有兴趣看看是否还有其他更好的解决方案。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

解决此类问题的常用方法是stuartsierra/componenttolitius/mount

完整的描述在这里不合时宜,但是一般的想法是拥有一些系统来管理状态,该系统允许干净地 reload 应用程序状态。当在正在运行的系统上进行交互工作时,这有助于保持与保存在源文件中的代码接近。

答案 3 :(得分:0)

感谢大家的建议。我发布了自己的解决方案,因为我已经找到了适合自己的前进方式,并且不确定上述任何内容是否正是我想要的。

我得出的结论是clojure REPL虽然有用,但并不是我进行测试的地方。基本上,可以选择在运行命令以清除每次测试运行之间的副本(例如tools.namespace https://github.com/clojure/tools.namespace中非常有用的refresh函数)还是不在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表示同意。

如果有人感兴趣,那么在https://youtu.be/-RaFcpNiYCo上有很多关于REPL如何保持状态以及如何引起各种奇怪行为的演讲。事实证明,我在重新定义函数时遇到的问题只是冰山一角。

答案 4 :(得分:0)

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