两者看起来都相当不错。我想了解每个库的特长或不足之处,特别是在测试web应用程序时。
发布于 2012-10-19 06:59:35
我没有使用speclj,而且我是Midje的第一作者。其他人没有提到的一点是,Midje试图利用函数式语言和面向对象语言之间的差异。
一个不同之处是不变性。因为大多数函数只依赖于它们的输入,而不依赖于包含的状态,所以您对它们所做的真实性陈述在感觉上与它们的面向对象的对应物是不同的。在OO测试中,您可以举例说明这样的形式:“给定这些历史记录和这些输入,此方法将生成某样东西。”
似乎函数式语言中的例子会更简单:“给定这些输入,这个函数返回某某”。但我不认为这是完全正确的。我认为系统中的其他功能扮演着类似于状态/历史的角色:它们是你试图获得智力控制的东西之一。函数及其关系是您希望测试帮助您清楚地思考的东西。
出于这个原因,编写Midje的假设是一个甜蜜的开发过程包括如下内容:
然后,在典型的mockist风格中,你大致是自顶向下或从外到内开发,当你从错误中恢复或有更好的想法时,允许不可避免的迭代。
最终结果是一大堆函数,它们的相互关系由测试或(如Midje所称)关于函数及其所依赖的函数的“事实”记录下来。许多人评论说Midje有一种Prolog/logic编程的感觉,这不是偶然的。像往常一样,测试是例子,但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的真值无关,除非它永远不会改变。元常量的概念是,它是一个除了在测试中显式声明的值之外,什么都不知道的值。在测试中,很难区分什么是必要的,什么是附带的。这有许多不好的影响:理解、可维护性等。元常量提供了清晰度。如果一个值是一个包含键:a的值3的映射或记录,这很重要,您可以明确地说:
(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中描述的那种“伦敦风格”的模拟繁重的测试开发,这是我在编写Ruby代码时经常使用的方法。但事实证明,它有一种相当不同的感觉,以一种难以描述的方式。但这种感觉需要更多的工具支持,而不仅仅是with-redefs。
其结果是,Midje在一定程度上是试图找到一种函数式TDD风格,而不仅仅是OO的一个端口。它也试图成为一个通用的工具,但它是一个半自以为是的软件。正如亚伯拉罕·林肯所说:“那些喜欢这种东西的人会发现这正是他们喜欢的东西。”
发布于 2012-10-18 10:36:00
使用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做些什么,但演示了它擅长的本质。在这里,您可以看到需要表达的外部复杂性非常少:
<代码>H111表示您不关心它接收的第二个参数是什么。<代码>H212<代码>G213
我试图用这个例子的主要观点是,它是一种非常干净和紧凑的方式来表达复杂代码的测试(我说的复杂是指它有可以分离的嵌入式部分),而不是试图一次测试所有东西。一次测试所有的东西都有它的位置,即在集成测试中。
我承认我是有偏见的,因为我经常使用midje,而我只看过speclj,但我的感觉是speclj可能对那些使用过类似的Ruby库并根据那次经验找到that way of thinking about tests理想的人最有吸引力。这是选择测试框架的一个很好的理由,而且可能还有其他好的方面,希望其他人可以评论一下。
发布于 2012-10-19 00:21:21
我肯定会选择Speclj。
Speclj易于集成和使用。它的语法没有Midje那么华丽,Speclj是基于RSpec的,它为你提供了所有Ruby程序员习惯的特性,同时又不会丢失Clojure的特性。
Speclj中的自动跑步器非常棒。
lein spec -a一旦你使用了一段时间,你就会想,当你不得不手动运行测试时,你是如何完成工作的。
模仿是不成问题的,因为你可以简单地使用-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))))))这种简单的模仿方法切中要害,没有误导。
至于测试web应用,Speclj运行得很好。事实上,Speclj支持已经内置于Joodo中。
免责声明:我写了Speclj
https://stackoverflow.com/questions/12928250
复制相似问题