Очистить состояние Cursive REPL перед каждым тестом - PullRequest
0 голосов
/ 12 ноября 2018

Я новичок в Cursive и Clojure в целом, и у меня возникли некоторые трудности с получением приличного рабочего процесса TDD.

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

(def sayHello "hello")

(deftest test-repl-state
  (testing "testing state in the repl"
      (is (= "hello" sayHello)))) 

Если вы запустите это с помощью «Tools-> REPL-> Выполнить тесты в текущих ns в REPL», оно пройдет.

Если вы затем выполните рефакторинг кода, как этот

(def getGreeting "hello")

(deftest test-repl-state
  (testing "testing state in the repl"
      (is (= "hello" sayHello))))  

Если вы запустите это с помощью «Tools-> REPL-> Выполнить тесты в текущих ns в REPL», оно все равно пройдет (потому что определение sayHello все еще существует в repl). Тем не менее, тесты должны провалиться, потому что код в настоящее время находится в состоянии сбоя (sayHello не определено где-либо в коде).

Я пытался переключить кнопку «местные жители будут очищены» в окне REPL, но, похоже, это не решает проблему.

Если есть способ запустить тесты вне REPL (или в новом REPL для каждого запуска теста), я бы согласился с этим в качестве решения.

Все, что я хочу, - это соответствие 1: 1 между тестируемым исходным кодом и результатом теста.

Заранее спасибо за помощь.

Ответы [ 5 ]

0 голосов
/ 17 ноября 2018

Один вариант, который может помочь, особенно если вы объединяете несколько утверждений или имеете повторяющиеся тесты, - let. Привязка «имя-значение» имеет известную область и может избавить вас от повторного набора текста.

Вот пример:

(deftest my-bundled-and-scoped-test
   (let [TDD    "My expected result"
         helper (some-function :data)]
     (testing "TDD-1: Testing state in the repl"
       (is (= TDD "MY expected result")))
     (testing "TDD-2: Reusing state in the repl"
       (is (= TDD helper)))))

После завершения теста my-bundled-and-scoped вы больше не будете в привязке let. Дополнительным преимуществом является то, что результат some-function также можно будет использовать повторно, что удобно для тестирования нескольких утверждений или свойств одной и той же пары функция / вход.

Что касается темы, я бы также порекомендовал использовать Leiningen для запуска ваших тестов, так как существует множество плагинов, которые могут помочь вам более эффективно тестировать. Я бы проверил тест-обновление , speclj и clo clos .

0 голосов
/ 15 ноября 2018

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

Я пришел к выводу, что закрытый REPL, хотя и полезен, не для того, чтобы я запускал тесты. Это в основном сводилось к выбору: либо запускать команду для очистки repl между каждым запуском теста (например, очень полезная функция refresh в tools.namespace https://github.com/clojure/tools.namespace), либо не запускать тесты в REPL.

Я выбрал последний вариант, потому что.

  1. Это на один шаг меньше (а перезагрузка не всегда идеальна)
  2. Тесты CI не запускаются в REPL, поэтому запуск их непосредственно в dev на один шаг ближе к среде CI.
  3. Производственный код также не запускается в REPL, поэтому выполнение тестов вне repl ближе к тому, как работает производственный код.

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

run tests within cursive

Я довольно неопытен с Clojure, а также с упрямым старым козлом, который настроен на его TDD, но, по крайней мере, некоторые другие согласны со мной об этом https://github.com/cursive-ide/cursive/issues/247.

Кроме того, если кому-то интересно, то здесь много говорят о том, как REPL поддерживает состояние и как это вызывает все виды странного поведения. https://youtu.be/-RaFcpNiYCo. Оказывается, проблема, с которой я столкнулся, определение функций было лишь верхушкой айсберга.

0 голосов
/ 12 ноября 2018

Вы можете использовать Встроенное тестовое сужение (селектор теста) функция test-refresh плагин lein.Он позволяет тестировать только те тесты, которые были отмечены мета ^:test-refresh/focus каждый раз, когда вы сохраняете файл.

0 голосов
/ 13 ноября 2018

Обычное решение для такого рода проблем: stuartsierra/component или tolitius/mount.

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

0 голосов
/ 12 ноября 2018

Да, раздражает наличие старых def s.Обычно я даже не создаю тесты (упс), но это укушает меня во время нормального развития.Если я создаю функцию, затем переименовываю ее, затем изменяю ее, а затем случайно обращаюсь к имени первой функции, я получаю странные результаты, поскольку она ссылается на старую функцию.Я все еще ищу хороший способ обойти это, не включая убийство и перезапуск REPL.

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

  • Откройте терминал IntelliJ (кнопка в левом нижнем углу окна) и запустите lein test.Это выполнит все тесты проекта и сообщит о результатах.

  • Как и выше, вы можете, кроме IntelliJ, открыть командное окно в каталоге проекта и запустить lein test,и он будет запускать все найденные тесты.

Вы также можете указать, какое пространство имен тестировать, используя lein test <ns here> (например, lein test beings-retry.core-test), или определенный тест в пространстве имен, используя * 1020.* (например, lein test :only beings-retry.core-test/a-test; где a-test - это deftest).К сожалению, этого не происходит в REPL, поэтому он как бы прерывает рабочий процесс.


Единственный известный мне обходной путь на основе REPL, как упоминалось выше, - просто убить REPL:

  • «Stop REPL» (Ctrl + F2)
  • «Reconnect» (Ctrl + F5).

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

...