Статьи

Clojure.test Введение

Я признаю это, первое, что мне нравится делать при изучении нового языка, это запустить REPL . Тем не менее, я обычно готов к следующему шагу после ввода нескольких чисел, строк и определения функции или двух.

Как и столетия назад, Майк Кларк написал статью об использовании модульного тестирования для изучения нового языка. Майк опередил свое время. Эта запись в блоге должна помочь вам, если вы хотите следовать совету Майка.

К счастью, Clojure имеет встроенную поддержку для простого тестирования. (В настоящее время я использую Clojure 1.2, вы можете скачать его с clojure.org )

Прежде чем мы начнем, давайте убедимся, что все работает Сохраните файл со следующим clojure и запустите * его с clojure.

(ns clojure.test.example
  (:use clojure.test))

(run-all-tests)

Если все в порядке, вы должны увидеть нечто похожее в следующем выводе.

Testing clojure.walk

Testing clojure.core

(a bunch of other namespaces tested)

Testing clojure.zip

Ran 0 tests containing 0 assertions.
0 failures, 0 errors.

Если вы зашли так далеко, у вас все готово, чтобы начать писать свои собственные тесты. Если у вас возникли какие-либо проблемы, я советую войти в чат-комнату #clojure IRC на Freenode.net

. Синтаксис для определения тестов очень прост. Следующий тест проверяет, что 1 + 1 = 2. Вы захотите добавить тест после определения ns и перед (run-all-tests) в файле, который вы только что создали.

(deftest add-1-to-1
  (is (= 2 (+ 1 1))))

Запуск теста должен дать что-то похожее на следующий результат.

Testing clojure.walk

Testing clojure.test.example

(a bunch of other namespaces tested)

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Мы видим все стандартные пространства имен clojure; тем не менее, мы видим наше пространство имен (clojure.test.example) также в результатах. Вывод внизу также говорит нам, что был выполнен 1 тест с 1 утверждением.

В следующем примере показано тестирование пользовательской функции добавления. (отсюда мы добавим дополнительные тесты, не удаляя старые тесты)

(defn add [x y] (+ x y))

(deftest add-x-to-y
  (is (= 5 (add 2 3))))

Если все идет по плану, то при выполнении ваших тестов должен появиться следующий текст в нижней части вывода.

Ran 2 tests containing 2 assertions.
0 failures, 0 errors.

На этом этапе вы можете указать несколько разных чисел, чтобы убедиться, что добавление работает, как и ожидалось.

(deftest add-x-to-y-a-few-times
  (is (= 5 (add 2 3)))
  (is (= 5 (add 1 4)))
  (is (= 5 (add 3 2))))

Запуск тестов показывает нам наш статус

Ran 3 tests containing 5 assertions.
0 failures, 0 errors.

Это прекрасно работает; однако clojure.test также предоставляет возможность проверки нескольких значений.

В следующем примере проверяются те же условия с использованием:

(deftest add-x-to-y-a-using-are
  (are [x y] (= 5 (add x y))
       2 3
       1 4
       3 2))

И неудивительные результаты.

Ran 4 tests containing 8 assertions.

Это просто есть; Тем не менее, вы можете делать все, что вам нужно в форме. Давайте возьмем значение из карты в качестве дополнительного примера.

(deftest grab-map-values-using-are
  (are [y z] (= y (:x z))
       2 {:x 2}
       1 {:x 1}
       3 {:x 3 :y 4}))

Оставив нас с

Ran 5 tests containing 11 assertions.

Это и
макросы будут все , что вам нужно для 90% всех тестов вы когда — либо хотите написать. За дополнительными утверждениями и более подробной информацией вы можете ознакомиться с
документацией
clojure.test .

Расширенные темы (очень необязательно, чтобы начать)

Меня раздражают шумы в результатах моего теста. Наши результаты были очень шумными из-за отчетности пространства имен. Функция run-all-tests принимает регулярное выражение (
описано здесь ). Мы можем изменить наш вызов выполнения теста, чтобы включить регулярное выражение, как показано в следующем примере.

(run-all-tests #"clojure.test.example")

Как только мы переключимся на регулярное выражение, результаты должны быть ограничены следующим выводом.

Testing clojure.test.example

Ran 5 tests containing 11 assertions.
0 failures, 0 errors.

Этот подход прекрасно работает для нашего текущего файла примера; однако, кажется, что лучшим решением было бы прекратить сообщать о пространствах имен, которые не содержат никаких тестов. Следующий фрагмент изменяет мультиметод отчета,
чтобы игнорировать пространства имен, которые не содержат никаких тестов.

(defmethod report :begin-test-ns [m]
    (with-test-out
      <b>(when (some #(:test (meta %)) (vals (ns-interns (:ns m))))</b>
        (println "\nTesting" (ns-name (:ns m))))))

Если вы только начинаете, не волнуйтесь, вам не нужно понимать, что происходит в этом фрагменте. Я скопировал оригинальный метод отчета и сделал его условным, добавив код жирным шрифтом. В результате пространство имен печатается только в том случае, если оно содержит какие-либо тесты.

Теперь, когда наши результаты чисты, давайте поговорим о способах получения этих результатов.

Добавление вызовов в функцию run-all-tests не составляет большого труда при работе с одним пространством имен; тем не менее, вам нужно быть умным, когда вы хотите запустить набор тестов. Мне сказали, что у
leiningen и Maven есть задачи, которые позволяют вам запускать все тесты. Вы можете начать там. Я в настоящее время не использую ни один, и я ленивый. Я тоже не хочу настраивать,
тем более, что все, что я хочу сделать, это запустить все мои тесты.

Оказывается, очень легко добавить хук отключения в Java. Итак, как простое решение, я запускаю все свои тесты из ловушки завершения работы Java.

(.addShutdownHook
 (Runtime/getRuntime)
 (proxy [Thread] []
   (run []
 (run-all-tests))))

В общем, я создаю test_helper.clj со следующим кодом.

(ns test-helper
  (:use clojure.test))

(defmethod report :begin-test-ns [m]
    (with-test-out
      (if (some #(:test (meta %)) (vals (ns-interns (:ns m))))
        (println "\nTesting" (ns-name (:ns m))))))

(.addShutdownHook
 (Runtime/getRuntime)
 (proxy [Thread] []
   (run []
 (run-all-tests))))

После того как вы создали test_helper.clj, вы можете использовать test-helper (так же, как вы использовали clojure.test) (пример ниже), и ваши тесты будут автоматически запускаться при выходе, и только выходные пространства имен с тестами будут включены в вывод.

Стоит отметить, что некоторые пространства имен clojure.contrib, похоже, содержат тесты, поэтому на практике я использую регулярное выражение, которое игнорирует все пространства имен, начинающиеся с «clojure» ** при запуске всех тестов. Объединяя все эти идеи, я обнаружил, что могу очень легко выполнить все свои тесты или только тесты в текущем пространстве имен.

Ниже вы можете найти весь код из этой записи.

clojure.test.example.clj

(ns clojure.test.example
  (:use clojure.test test-helper))

(deftest add-1-to-1
  (is (= 2 (+ 1 1))))

(defn add [x y] (+ x y))

(deftest add-x-to-y
  (is (= 5 (add 2 3))))

(deftest add-x-to-y-a-few-times
  (is (= 5 (add 2 3)))
  (is (= 5 (add 1 4)))
  (is (= 5 (add 3 2))))

(deftest add-x-to-y-a-using-are
  (are [x y] (= 5 (add x y))
       2 3
       1 4
       3 2))

(deftest grab-map-values-using-are
  (are [y z] (= y (:x z))
       2 {:x 2}
       1 {:x 1}
       3 {:x 3 :y 4}))

test_helper.clj

(ns test-helper
  (:use clojure.test))

(defmethod report :begin-test-ns [m]
    (with-test-out
      (if (some #(:test (meta %)) (vals (ns-interns (:ns m))))
        (println "\nTesting" (ns-name (:ns m))))))

(.addShutdownHook
 (Runtime/getRuntime)
 (proxy [Thread] []
   (run []
 (run-all-tests))))

* Запуск файла clojure должен быть таким простым: java -cp /path/to/clojure.jar clojure.main -i file.to.run.clj

** (run-all-tests # # [^ (clojure)] . * «); осторожно, теперь ваши тесты clojure.test.example будут игнорироваться. Не позволяйте этому сбить вас с толку.

С http://blog.jayfields.com/2010/08/clojuretest-introduction.html