Я признаю это, первое, что мне нравится делать при изучении нового языка, это запустить 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