Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 在Clojure中对接口进行测试_Unit Testing_Clojure - Fatal编程技术网

Unit testing 在Clojure中对接口进行测试

Unit testing 在Clojure中对接口进行测试,unit-testing,clojure,Unit Testing,Clojure,在Java世界中,当涉及到开发单元测试时,我遵循了一种“测试接口”的方法。这意味着,如果我有一个Java接口,我将为该接口编写一个单元测试类(从JUnit的TestCase或其他扩展而来);测试该接口。这个类是抽象的,包含一系列测试接口方法的测试方法。下面是一个简单的例子: /** my interface */ public interface MyFooInterface { int foo(); String bar(); } /** some implementatio

在Java世界中,当涉及到开发单元测试时,我遵循了一种“测试接口”的方法。这意味着,如果我有一个Java接口,我将为该接口编写一个单元测试类(从JUnit的TestCase或其他扩展而来);测试该接口。这个类是抽象的,包含一系列测试接口方法的测试方法。下面是一个简单的例子:

/** my interface */
public interface MyFooInterface {
    int foo();
    String bar();
}

/** some implementation */
public class MyFooImplA implements MyFooInterface {
    public int foo() { ... }
    public String bar() { ... }
}

/** some other implementation */
public class MyFooImplB implements MyFooInterface {
    public int foo() { ... }
    public String bar() { ... }
}

/** my test case for my interface */
public abstract TestMyFooInterface extends TestCase {

    private MyFooInterface objUnderTest;

    public abstract MyFooInterface getMyFooInterface();

    public void setUp() {
        objUnderTest = getMyFooInterface();
    }

    public void testFoo() {
        ... bunch of assertions on 'objUnderTest'...
    }

    public void testBar() {
        ... bunch of assertions on 'objUnderTest'...
    }
}

/** concrete test class, with very little work to do */
public TestMyFooImplA extends TestMyFooInterface {
    public MyFooInterface getMyFooInterface() {
        return new MyFooImplA();
    }
}

/** another concrete test class, with very little work to do */
public TestMyFooImplB extends TestMyFooInterface {
    public MyFooInterface getMyFooInterface() {
        return new MyFooImplB();
    }
}
所以这里我们有一件很棒的事情。无论我们有多少MyFooInterface的实现,我们只需要编写一组单元测试(在TestMyFooInterface.java中),以确保MyFooInterface的契约正确性。然后,我们只需要为每个接口实现提供一个具体的测试用例。这些具体的测试用例很无聊;他们所需要做的就是提供一个“getMyFooInterface”的实现;他们只是通过构造正确的实现类来实现这一点。现在,当我运行这些测试时,TestMyFooInterface中的每个测试方法都将为每个具体的测试类调用。顺便说一句,当我说“当我运行这些测试时”,这意味着将创建TestMyFooImplA的一个实例(因为它是由测试工具发现的一个具体测试用例;基于Ant或Maven的东西或任何东西),并且将运行它的所有“测试”方法(即TestMyFooInterface中的所有方法)。TestMyFooImplB也将被实例化,其“test”方法将被运行。砰!我们只需要编写一组测试方法,它们将针对我们创建的每个具体测试用例实现运行(只需要几行代码!)

好吧,我想在Clojure中反映同样的方法,当涉及到协议和记录时,但我有点绊倒了。此外,我想验证这种方法在Clojure世界中是否合理

以下是我目前在Clojure的情况。这是我的“界面”:

现在我可能有两种不同的协议实现,以记录的形式。下面是一个实现:

(ns myfoo-a-impl
    (:use [myabstractions]))

(defrecord MyFooAImplementation [field-a field-b]
    MyFooProtocol
    (foo [this] ...impl here...)
    (bar [this] ...impl here...))
(ns myfoo-b-impl
    (:use [myabstractions]))

(defrecord MyFooBImplementation [field-1 field-2]
    MyFooProtocol
    (foo [this] ...impl here...)
    (bar [this] ...impl here...))
(ns myfooBTests
    (:use [myfooprotocol-tests :only [testFoo testBar]])
    (:import [myfoo_b_impl MyFooAImplementation])
    (:use [abstractions])
    (:require [myfoo-b-impl])
    (:use [clojure.test]))

(deftest testForFoo []
    (is (testFoo myfoo-b-impl/foo (MyFooBImplementation. '1 '2))))

(deftest testForBar []
    (is (testBar myfoo-b-impl/bar (MyFooBImplementation. '1 '2))))
以及另一项实施:

(ns myfoo-a-impl
    (:use [myabstractions]))

(defrecord MyFooAImplementation [field-a field-b]
    MyFooProtocol
    (foo [this] ...impl here...)
    (bar [this] ...impl here...))
(ns myfoo-b-impl
    (:use [myabstractions]))

(defrecord MyFooBImplementation [field-1 field-2]
    MyFooProtocol
    (foo [this] ...impl here...)
    (bar [this] ...impl here...))
(ns myfooBTests
    (:use [myfooprotocol-tests :only [testFoo testBar]])
    (:import [myfoo_b_impl MyFooAImplementation])
    (:use [abstractions])
    (:require [myfoo-b-impl])
    (:use [clojure.test]))

(deftest testForFoo []
    (is (testFoo myfoo-b-impl/foo (MyFooBImplementation. '1 '2))))

(deftest testForBar []
    (is (testBar myfoo-b-impl/bar (MyFooBImplementation. '1 '2))))
所以在这一点上,我的位置和我熟悉的OOJava世界中的位置差不多。我有两个MyFooProtocol协议的实现。每个实现的“foo”和“bar”功能都应该遵守MyFooProtocol中记录的功能契约

在我看来,我只想为“foo”和“bar”创建一组测试,即使我有多个实现,就像我在Java示例中所做的那样。下面是我接下来用Clojure代码做的事情。我创建了我的测试:

(ns myfooprotocol-tests)

(defn testFoo [foo-f myFoo]
    (let [fooResult (foo-f myFoo)]
      (...some expression that returns a boolean...)))

(defn testBar [bar-f myBar]
    (let [barResult (bar-f myBar)]
      (...some expression that returns a boolean...)))
太好了,我只写过一次测试。上面的每个函数都返回一个布尔值,有效地表示一些测试用例/断言。事实上,我会有很多很多(对于我想做的每一个断言)。现在,我需要创建我的“实现”测试用例。因为Clojure不是OO,所以我不能像上面的Java示例那样做,所以这就是我的想法:

(ns myfooATests
    (:use [myfooprotocol-tests :only [testFoo testBar]])
    (:import [myfoo_a_impl MyFooAImplementation])
    (:use [abstractions])
    (:require [myfoo-a-impl])
    (:use [clojure.test]))

(deftest testForFoo []
    (is (testFoo myfoo-a-impl/foo (MyFooAImplementation. 'a 'b))))

(deftest testForBar []
    (is (testBar myfoo-a-impl/bar (MyFooAImplementation. 'a 'b))))
现在来看另一个测试用例实现:

(ns myfoo-a-impl
    (:use [myabstractions]))

(defrecord MyFooAImplementation [field-a field-b]
    MyFooProtocol
    (foo [this] ...impl here...)
    (bar [this] ...impl here...))
(ns myfoo-b-impl
    (:use [myabstractions]))

(defrecord MyFooBImplementation [field-1 field-2]
    MyFooProtocol
    (foo [this] ...impl here...)
    (bar [this] ...impl here...))
(ns myfooBTests
    (:use [myfooprotocol-tests :only [testFoo testBar]])
    (:import [myfoo_b_impl MyFooAImplementation])
    (:use [abstractions])
    (:require [myfoo-b-impl])
    (:use [clojure.test]))

(deftest testForFoo []
    (is (testFoo myfoo-b-impl/foo (MyFooBImplementation. '1 '2))))

(deftest testForBar []
    (is (testBar myfoo-b-impl/bar (MyFooBImplementation. '1 '2))))
我的两个具体测试实现(myFooATests和myFooBTests命名空间)看起来很冗长,但它们真正做的只是将断言逻辑委托给myfooprotocol测试命名空间中的“testFoo”和“testBar”函数。这只是样板代码

但是有一个障碍。在最后两个清单中,“testFoo”和“testBar”的第一个参数是“myfoo-#-impl/foo”或“myfoo-#-impl/bar”(其中“#”是a或b)。但这不起作用,因为'foo'和'bar'函数被隐藏在defprotocol中,我不能以这种方式访问它们

因为我正在孤立地学习Clojure,所以我想接触SO社区并尝试获得一些帮助。首先,我在Clojure代码中所做的看起来有点合理吗?也就是说,尝试“测试接口(错误,协议)一次”的想法——这在Clojure世界中是一个值得追求的目标吗?(我内心的枯燥是这样说的;我内心的OO实践者也是如此)。如果我对Clojure中协议和记录之间关系的解释是正确的(即接口和实现伙伴的一种形式),那么我真的只想编写一次测试(就像我在“myfooprotocol tests”命名空间中尝试做的那样)

第二,假设所有这些都是合理的,我如何有效地传递在“myfoo-a-impl”和“myfoo-b-impl”命名空间的defrecords中定义的“foo”和“bar”函数?获取它们的语法是什么


谢谢您的时间。

首先是简单的部分-是的,您测试协议的各种实现的想法是有意义的,而且非常有用

现在是非常简单的部分,即如何进行。将协议视为在命名空间中创建函数(理论上还没有实现,因为扩展该协议时会发生这种情况)。所以当你说:

(ns myabstractions)

(defprotocol MyFooProtocol
    (foo [this] "returns some int")
    (bar [this] "returns some string"))
这意味着现在myabstractions有两个函数,分别称为foo和bar。由于它们只是函数,我们可以从这个名称空间轻松地引用它们,即
myabstractions/foo
myabstractions/bar
。这清楚地表明,您不需要将这些函数传递给泛型测试命名空间函数,它们只需要一个类型(在您的情况下是一个记录),可以在该类型上调用foo或bar,因此:

(ns myfooprotocol-tests (:use [myabstractions]))

(defn testFoo [myFoo]
    (let [fooResult (foo myFoo)]
      (...some expression that returns a boolean...)))

(defn testBar [myBar]
    (let [barResult (bar myBar)]
      (...some expression that returns a boolean...)))

从每个实现的特定测试中,您只需要通过实现协议的记录实例。

首先是简单的部分-是的,您测试协议的各种实现的想法是有意义的,而且非常有用

现在是非常简单的部分,即如何进行。将协议视为在命名空间中创建函数(理论上还没有实现,因为扩展该协议时会发生这种情况)。所以当你说:

(ns myabstractions)

(defprotocol MyFooProtocol
    (foo [this] "returns some int")
    (bar [this] "returns some string"))
这意味着现在myabstractions有两个函数,分别称为foo和bar。因为它们只是函数,所以我们可以很容易地从这个名称空间引用它们,即
myabstractions/foo