Тестирование функций CRUD Clojure, например, с ruby ​​Rspec

Я только что закончил свои первые шесть недель работы с 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

но я думаю, что еще нет «стандартного» способа выполнить тест, включая часть БД (CRUD) в Clojure. Я даже не знаю, куда положить файлы спецификации. Я вижу библиотеки Clojure, похожие на FactoryGirl, но я не знаю, должен ли я создавать свои собственные структуры данных со спецификацией, поэтому я не уверен, с чего начать, есть clojure.test.check.generators и генераторы спецификаций, но я не знаю узнайте, отличаются ли они или мне следует использовать только спецификацию, но не clojure.test.check. Могу ли я запустить тест из командной строки, а не внутри REPL?

Я имею в виду: есть ли документ или учебник о том, как протестировать набор функций CRUD? Я думаю, что мне просто нужен начальный HOWTO, а затем я мог бы взять его оттуда и написать учебник для новичков, таких как я.

ОБНОВЛЕНО:

Похоже, Midje - это то, что я ищу:

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


person aarkerio    schedule 30.07.2018    source источник


Ответы (3)


В Clojure идиоматично подталкивать ввод-вывод к краям вашего приложения. Вместо чтения из БД внутри вашей функции загрузки вы передаете данные, считанные из БД, в вашу функцию загрузки в карте параметров. Затем вы пишете свои тесты против чистой части.

Ваша функция в конечном итоге будет выглядеть так:

(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))
person Andrew Slough    schedule 01.08.2018
comment
Что касается моего собственного ответа, в этом случае вам не нужно будет издеваться над model-upload/get-upload при тестировании download-without-db. Теперь этот код все еще недостаточно хорош, потому что get-upload-from-db все еще имеет побочные эффекты и его трудно протестировать, вы хотите изменить его, чтобы он принимал загрузку в качестве аргумента. Затем вы также можете протестировать его без насмешек. Наконец, download должен позвонить model-upload/get-upload. Но вам не нужен модульный тест для download, вместо этого вам нужен целочисленный тест. И теперь вы устранили всю потребность в моках. - person Didier A.; 08.08.2018

Вы просто ищете 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 не объектно-ориентированный, поэтому здесь нет объектов для имитации. Тем не менее, вы часто используете форму Java в Clojure, а Java предоставляет классы и объекты. Если вы хотите легко смоделировать их, вы можете использовать среду для имитации Java под названием Mockito.

Однако в вашем случае функция download не использует никаких объектов Java. Так что и вам не надо.

Теперь, если вы хотите, чтобы это был интеграционный тест, написанный мной тест вам подойдет. Если вы хотите, чтобы это был модульный тест, и я предполагаю, что (model-upload/get-upload id) выполняет некоторый ввод-вывод, вам нужно смоделировать функцию 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})))))))

Или вы можете использовать с-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})))))))
person Didier A.    schedule 08.08.2018
comment
@andrew-slough прав, однако вы также должны попытаться разделить свои побочные эффекты на их собственные функции, которые будет легче протестировать, поскольку вам не нужно будет так много издеваться. - person Didier A.; 08.08.2018

Вот несколько онлайн-ресурсов для вас:

и ниже приведен пример того, как мне нравится структурировать общие тесты (но не CRUD):

person Alan Thompson    schedule 30.07.2018