как издеваться над конкретной реализацией протокола? - PullRequest
0 голосов
/ 20 февраля 2019

Как смоделировать конкретную реализацию протокола, желательно с простым Clojure, т.е. без внешних библиотек, например Midje?Я знаю, как смоделировать все реализации, но не конкретные:

(defprotocol P
  (foo [p]))
(defrecord R1 [])
(defrecord R2 [])
(extend-protocol P
  R1
  (foo [_] :r1)
  R2
  (foo [_] :r2))

(foo (->R1)) ;; :r1
(foo (->R2)) ;; :r2

;; now the mocking ...
(with-redefs [foo (constantly :redefed)]
  (println (foo (->R1)))  ;; :redefed
  (println (foo (->R2)))) ;; :redefed

т.е. как заставить (foo (->R1)) вернуть :redefed, в то время как (foo (->R2)) все еще возвращает :r2?

Предположим, я могу вносить изменения только под комментарием now the mocking ....Обратите внимание, что я откладываю рекомендацию о расширении протокола, только если у меня есть контроль над протоколом, типом или обоими.

Ответы [ 4 ]

0 голосов
/ 21 февраля 2019

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

0 голосов
/ 20 февраля 2019

Не передавайте foo R1 вообще.Определите новую реализацию, R1Mocked, которая делает все, что вы хотите, и передайте ее вместо этого.Это именно тот тип полиморфизма, который поддерживаются протоколами.

0 голосов
/ 21 февраля 2019

Вы можете создать новый протокол, содержащий методы с той же сигнатурой, и использовать его при повторном связывании:

(def foo-orig foo)

(defprotocol Q
  (bar [q]))
(extend-protocol Q
  R1
  (bar [_] :redefed)
  R2
  (bar [this] (foo-orig this)))

Обратите внимание, что вам придется захватить исходное определение foo для реализаций, которые вы не делаетехочу изменить.Затем:

(with-redefs [foo bar]
              (println (foo (->R1)))   ;; :redefed
              (println (foo (->R2))))  ;; :r2

или вы можете определить мультиметод, например

(defmulti bar (fn [q] (type q)))
(defmethod bar R1 [_]
  :redefed)
(defmethod bar :default [q]
  (foo-orig q))
0 голосов
/ 20 февраля 2019

Моя первая мысль состоит в том, чтобы иметь делегат foo для помощника fn:

(extend-protocol P
  R1
  (foo [p] (foo-r1 p))
  R2
  (foo [p] (foo-r2 p)))

с

(defn foo-r1 [_] :r1)
(defn foo-r2 [_] :r2)

, а затем просто переопределить foo-r1 и foo-r2независимо от желаемого.

Обратите также внимание, что with-redefs предназначен для работы с var экземплярами, в то время как foo, который вы определяете как часть протокола, не совпадает с var.Это может быть причиной вашей глобальной проблемы переопределения.


Обновление

Возможно, вам потребуется уточнить ваш вариант использования и обновить вопрос.См. эту часть на clojure.org :

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

...