Midje和Speclj如何比较?

时间:2012-10-17 06:16:46

标签: clojure

两者看起来都相当不错。我想了解每个库特别擅长或缺乏的内容,特别是对于Web应用程序的测试。

5 个答案:

答案 0 :(得分:17)

我没有使用过speclj,我是Midje的第一作者。其他人没有提到的一点是,Midje试图利用功能语言和面向对象语言之间的差异。

一个区别是不变性。因为大多数函数仅依赖于它们的输入,而不依赖于包含的状态,所以你对它们所做的真实陈述与它们面向对象的对应物的感觉不同。在OO测试中,您可以使用以下形式的示例:“给定此历史记录和这些输入,此方法会产生这样的结果。”

似乎功能语言中的例子只是更简单的例子:“给定这些输入,这个函数会返回这样的东西”。但我不认为这是对的。我认为系统中的其他功能扮演的角色类似于州/历史:它们是你试图获得智力控制的东西之一。函数及其关系是您希望测试帮助您清楚思考的事情。

出于这个原因,Midje是在一个甜蜜的开发过程涉及说:

的假设下编写的
  • 我希望这个功能是什么?在系统的世界中,想一想这个函数的作用是什么好方法?
  • 在这样做的过程中,还有哪些其他功能有用---会捕获域的一个重要部分 - 以及我想对它们做出哪些真实陈述?

然后,在典型的嘲讽风格中,你大致自上而下或从外到内发展,允许在你从错误中恢复或有更好的想法时进行不可避免的迭代。

最终的结果是成为一大堆功能,它们的相互关系由测试记录,或者(如Midje所称)是关于功能及其依赖的功能的“事实”。不同的人评论说,Midje有一个Prolog /逻辑编程的感觉,这不是一个意外。和往常一样,测试就是例子,但是Midje试图让它们更像是真实的陈述。这是其唯一真正的创新功能,即metaconstants的理由。以下是他们的一个例子:

(fact "right changes the direction, but not the position"
  (right (snapshot north ...position...)) => (snapshot west ...position...)
  (right (snapshot east ...position...)) => (snapshot north ...position...)
  (right (snapshot south ...position...)) => (snapshot east ...position...)
  (right (snapshot west ...position...)) => (snapshot south ...position...))

在这种情况下,实际位置与函数right的真实位置无关,除非它永远不会改变。一个metaconstant的想法是,除了测试中明确说明的内容之外,它是什么都不知道的值。经常在测试中,很难说出什么是必要的,什么是偶然的。这有很多不好的影响:理解,可维护性等。主要信息提供清晰度。如果值是包含键3的值:a的地图或记录很重要,请明确说明:

(fact
  (full-name ..person..) => "Brian Marick"
  (provided
     ..person.. =contains=> {:given-name "Brian", :family-name "Marick"}))

这个测试明确指出了关于人的重要性 - 并明确了解重要的事情(除了这两个名字之外的任何事物)。

在数学术语中,Midje试图让你做出像“for all x where x ...”这样的陈述,同时仍然是一个测试工具,而不是一个定理证明者。

这种方法的灵感来自Growing Object-Oriented Software中描述的“伦敦式”模拟重型TDD,这是我在编写Ruby代码时经常使用的方法。但事实证明,这种感觉有着截然不同的感觉。但感觉需要更多的工具支持,而不仅仅是with-redefs

结果是,Midje试图找到一种功能性TDD,而不仅仅是OO TDD的端口。它也试图成为一种通用工具,但它是半固定的软件。正如亚伯拉罕·林肯所说:“那些喜欢这类事情的人会发现这是他们喜欢的事情。”

答案 1 :(得分:8)

使用Midje的最大好处是它可以提供有针对性的抽象测试,而无需测试所有零件,这些零件往往会拖累整个世界。

如果你有一个函数涉及调用辅助函数来生成时间戳,将某些东西放在数据库或消息队列中,发出API请求,缓存某些东西,记录某些东西等,你想知道这些涉及世界的功能调用发生了(有时发生了多少次),但实际执行它们与您正在测试的函数无关,被调用的函数通常应该进行自己的单元测试。

假设你的代码中有这个:


(defn timestamp [] (System/currentTimeMillis))

(defn important-message [x y] (log/warnf "Really important message about %s." x))

(defn contrived [x & y]
  (important-message x y)
  {:x x :timestamp (timestamp)})

以下是使用midje进行测试的方法:


(ns foo.core-test
  (:require [midje.sweet :refer :all]
            [foo.core :as base]))

(fact
 (base/contrived 100) => {:x 100 :timestamp 1350526304739}
 (provided (base/timestamp) => 1350526304739
           (base/important-message 100 irrelevant) => anything :times 1))

这个例子只是对你能用midje做些什么的快速一瞥,但展示了它擅长的本质。在这里你可以看到表达所需要的非常复杂的事情:

  1. 该功能应该产生什么(尽管事实上是什么 每次你生成的时间戳函数会有所不同 调用函数),
  2. 调用了时间戳函数和日志记录函数,
  3. 日志功能只被调用一次,
  4. 日志记录功能收到了预期的第一个参数,
  5. 你不关心收到的第二个论点是什么。
  6. 我试图用这个例子做的主要观点是,它是一种非常干净和紧凑的方式来表达复杂代码的测试(并且复杂的意思是它具有可以分离的嵌入部分),而不是尝试一次测试一切。一次性测试所有内容都有其地位,即集成测试。

    我当然有偏见,因为我积极使用midje,而我只看了speclj,但我的感觉是speclj可能对使用类似Ruby库并找到that way of thinking about tests理想的人最有吸引力在那次经历上。这是选择测试框架的一个完全可靠的理由,并且可能还有其他很好的事情,希望其他人可以评论。

答案 2 :(得分:8)

我绝对会选择Speclj

Speclj易于集成和使用。它的语法不像Midje那样华丽。 Speclj基于RSpec,为您提供Ruby程序员习惯的所有配置,而不会丢失Clojure的特性。

Speclj的赛车运动员很棒。

lein spec -a

一旦你使用了一段时间,你就会想知道在必须手动运行测试时你是如何完成工作的。

模拟是一个非问题,因为你可以简单地使用with-redefs。 @ rplevy在Speclj中的例子看起来像这样。

(ns foo.core-spec
  (:require [speclj.core :refer :all ]
            [foo.core :as base]))

(describe "Core"
  (it "contrives 100"
    (let [message-params (atom nil)]
      (with-redefs [base/timestamp (fn [] 1350526304739)
                    base/important-message #(reset! message-params [%1 %2])]
        (should= {:x 100 :timestamp 1350526304739} (base/contrived 100))
        (should= 100 (first @message-params))))))

这种愚蠢的嘲弄方法是至关重要的;没有误导。

至于测试网络应用程序,Speclj工作正常。实际上,Speclj支持构建到Joodo

免责声明:我写了Speclj

答案 3 :(得分:4)

我会说Midje特别擅长创建用于表达存根和嘲弄的DSL。如果你关心存根和嘲弄,并且想要大量使用它,我会选择Midje而不是Speclj,因为它有表达那些类型的测试的抽象,这些测试比他的答案中提供的slagyr方法更简洁。

如果你想要更轻量级的方法,另一个选择是Conjure存根/模拟库,用于与clojure.test一起使用。

Speclj闪耀的地方与RSpec非常相似,包括'describe'和'it'...... Midje实际上可以支持嵌套的事实,但不像Speclj那样优雅。

免责声明:我是Midje和Conjure的贡献者。 :)

答案 4 :(得分:1)

我建议Midje超过Speclj
对于speclj,我不认为它是否对mocks有很好的支持,与Midje相比,文档看起来也很稀疏。

Midje的语法也更好:

(foo :bar) => :result compared to (should= (foo :bar) :result)