【问题标题】:Testing a CRUD Clojure functions like with ruby Rspec使用 ruby​​ Rspec 测试 CRUD Clojure 函数
【发布时间】:2018-07-30 21:10:21
【问题描述】:

我刚刚完成了使用 Clojure 的前六周工作,到目前为止,我对这种语言非常满意。我正在使用 leiningen 和 PostgreSQL 开发我的个人博客。我已经可以发布新内容、上传文件并且我有会话、cookie 和角色,无论如何我认为此时我有足够的代码开始担心测试部分,但我有点卡住了,因为看起来很多clojure 的测试和规范方面正在发生一些事情。

所以我有这个功能:

(defn download
  "GET /admin/uploads/download/:id"
 [params]
 (let [id       (-> params :id)
       upload   (model-upload/get-upload id)
       filename (:filename upload)
       body     (clojure.java.io/file (str "public/uploads/" filename))]
{:status 200
 :body body
 :headers {"Content-Type" "application/pdf"
           "Content-Length" (str (.length body))
           "Cache-Control" "no-cache"
           "Content-Disposition" (str "attachment; filename=" filename)}}))

该函数将地图作为参数,并提供最终地图以由 compojure 发送和处理。我来自 Rails 世界,所以在 Rails 中测试这个功能的方法是创建一个 FactoryGirl 类,使用经典创建一个 Rspec 模型文件:

 expect(first_map).to eq(map_returned_by_function)

在其中比较预期的内容,然后从命令行运行 rspec 以获得绿线或红线。

从昨天开始,我一直在尝试使用 Clojure 使用此文档复制该过程:

https://www.codesai.com/2018/03/kata-generating-bingo-cards

但我认为目前还没有一种“标准”方式来进行测试,包括 Clojure 中的 DB (CRUD) 部分。我什至不知道将规范文件放在哪里。我看到类似于 FactoryGirl 的 Clojure 库,但我不知道是否应该使用规范创建自己的数据结构,所以我不确定从哪里开始,有 clojure.test.check.generators 和规范生成器,但我不知道知道它们是否不同,或者我是否应该只使用规范而不是 clojure.test.check。我可以从命令行而不是在 REPL 中运行测试吗?

我的意思是:是否有关于如何测试一组 CRUD 功能的文档或教程?我想我只需要最初的 HOWTO,然后我可以从那里拿走它,我会为像我这样的新手写一个教程。

更新:

看起来 Midje 就是我要找的东西:

https://github.com/marick/Midje/wiki/A-tutorial-introduction

【问题讨论】:

    标签: clojure clojure.spec


    【解决方案1】:

    在 Clojure 中将 IO 推送到应用程序的边缘是惯用的。不是从下载函数中的数据库读取,而是将从数据库读取的数据传递到参数映射中的下载函数。然后针对纯部分编写测试。

    你的函数最终会是这样的:

    (defn download-without-db
    "GET /admin/uploads/download/:id"
     [params]
     (let [upload   (-> params :upload)
           filename (:filename upload)
           body     (clojure.java.io/file (str "public/uploads/" filename))]
    {:status 200
     :body body
     :headers {"Content-Type" "application/pdf"
               "Content-Length" (str (.length body))
               "Cache-Control" "no-cache"
               "Content-Disposition" (str "attachment; filename=" filename)}}))
    
    (defn get-upload-from-db [params]
        (assoc params :upload (-> params :id model-upload/get-upload)))
    
    (defn download [params]
        (-> params 
            get-upload-from-db
            download-without-db))
    

    【讨论】:

    • 参考我自己的答案,在这种情况下,您在测试download-without-db 时不需要模拟model-upload/get-upload。现在这段代码还不够好,因为get-upload-from-db 仍然有副作用并且很难测试,你想改变它,让它把上传作为参数。然后你也可以在不模拟的情况下对其进行测试。最后,download 应该调用model-upload/get-upload。但是您不需要对download 进行单元测试,而是需要对其进行集成测试。您现在已经消除了对模拟的所有需求。
    【解决方案2】:

    您只是在寻找clojure.test。它甚至在其文档中提到了与 RSpec 的关系。

    这是 Clojure 本身的一部分,不需要依赖项,我建议您在使用像 Midje 这样的非标准测试框架之前先熟悉它,因为它是 Clojure 的事实上的测试框架,并且最受欢迎的。

    你会这样写一个测试:

    (deftest download
      (testing "With valid input"
        (testing "it should return a header map with filename included"
          (is (= first_map (unit/download {:id 1}))))))
    

    现在,Clojure 不是面向对象的,所以没有要模拟的对象。也就是说,您经常在 Clojure 中使用 Java 表单,而 Java 提供了类和对象。如果您想轻松地模拟它们,可以使用名为 Mockito 的 Java 模拟框架。

    但在您的情况下,download 函数不使用任何 Java 对象。所以你也不需要。

    现在,如果您希望这是一个集成测试,那么我编写的测试对您来说已经足够好了。如果您希望这是一个单元测试,并且我假设 (model-upload/get-upload id) 执行了一些 IO,您将需要模拟 model-upload/get-upload 函数。您可以使用with-redefs-fn 轻松做到这一点:

    (deftest download
      (testing "With valid input"
        (testing "it should return a header map with filename included"
          (with-redefs-fn {#'model-upload/get-upload (constantly {:filename "cool.pdf"})}
            (is (= first_map (unit/download {:id 1})))))))
    

    或者你可以使用with-redefs:

    (deftest download
      (with-redefs [model-upload/get-upload (constantly {:filename "cool.pdf"})]
        (testing "With valid input"
          (testing "it should return a header map with filename included"
            (is (= first_map (unit/download {:id 1})))))))
    

    【讨论】:

    • @andrew-slough 是正确的,但您还应该尝试将副作用拆分到它们自己的函数中,这样更容易测试,因为您不需要模拟那么多。跨度>
    【解决方案3】:
    猜你喜欢
    • 1970-01-01
    • 2018-02-21
    • 2016-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多