Re: Mocking multimethods

2010-12-22 Thread Meikel Brandmeyer
Hi,

maybe a different approach could be to use a richer datatype than a function, 
which carries both: the command and the undo command.

(deftype Command [action undo])

Then you could do something like:

(defn do-patch!
  [command args]
  (dosync
(let [patch {:command command :args (vec args)}]
  (apply (.action command) args)
  (alter patches- conj patch)
  patch)))

(defn undo-patch!
  []
  (dosync
((.undo (:command patch)))
(alter patches- pop)))

Then you could provide specially crafted Commands and there would not be the 
need to stub anything. Also Commands wouldn't have to be global objects.

Sincerely
Meikel

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: Mocking multimethods

2010-12-22 Thread Brian Marick
I think I misunderstand the issue, because this works for me:

(ns midje.util.git
  (:use [midje.sweet]))

;; Code except for undo-patch is the same as before.
;; ...

;; I pulled remove-patch out of undo-patch because I was
;; getting a screwy read error I didn't want to figure out 
;; at 5 in the morning, but I'd probably do that anyway:
;; http://codebetter.com/jeremymiller/2006/12/03/composed-method-pattern/

(defn remove-patch [patch]
  (alter patches- #(remove (fn [p] (= patch p)) %)))

(defn undo-patch [patch]
 (let [fn (undo-fn patch)]
   (dosync
 (fn)
 (remove-patch patch

(fact The patch's undo-fn is called for its side effect and the patch is 
forgotten
  (let [visible-evidence-of-a-side-effect (atom nil)]
(undo-patch ...patch...) = anything
(provided
  (undo-fn ...patch...) = (fn [] (reset! visible-evidence-of-a-side-effect 
:happened!))
  (remove-patch ...patch...) = :nothing-of-interest)
@visible-evidence-of-a-side-effect = :happened!))

1855 $ lein midje midje.util.git
All claimed facts (2) have been confirmed. 
1856 $ 


-
Brian Marick, Artisanal Labrador
Contract programming in Ruby and Clojure
Author of /Ring/ (forthcoming; sample: http://bit.ly/hfdf9T)
www.exampler.com, www.exampler.com/blog, www.twitter.com/marick

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: Mocking multimethods

2010-12-22 Thread Alyssa Kwan
I'd like to discuss this design approach.  (It's unrelated to the
testing issue.)  I avoided this design because the undo-fn is
determined at do-patch! time.  The use case is for a persistent system
like Git where patches may be undone long after being done - e.g. long
after the patch is written to the database.  In the meantime, the
system may be upgraded and the undo-fn for a patch may change.  If the
undo-fn and args is written to the database at do-patch! time, for
backwards compatibility, at undo-patch! time, the undo-fn would have
to be updated to whatever the new call is.

Contrast that with my current approach where only the fn is saved and
the undo-fn is determined at undo-patch! time.  For backwards
compatibility, the undo-fn would have to support legacy fn formats,
which I conjecture are slightly less likely to change than the
corresponding undo-fn.  This is a pure guess and is not based on real
requirements yet; I don't know that fns themselves are necessarily
more stable than their counterpart undo-fns.  It's a vague sense that
undo-fns are more subject to bug fixes:  fns themselves are processed
right away at do-patch! time, while undo-fns are like time bombs that
lay dormant until a patch has to be undone, long after the initial
patch.

In addition, in the current implementation I outlined, the undone
patch disappears.  The real implementation will most likely be a patch
with undo-patch! as the :fn and the patch being undone as the :arg.
This is necessary to support merging with remote branches like in
Git.  undo-patch!'s may themselves be undone, which would require 2
degrees of backwards compatibility:  converting the undo-fn in the
first undo, and converting the fn in the second undo.  Both approaches
have the same 2 degrees of backwards compatibility, but the command
approach still somehow seems more fragile.

I'd love to get your thoughts on this.  Anyone with experience working
on a similar system?

Thanks!
Alyssa

On Dec 22, 4:35 am, Meikel Brandmeyer m...@kotka.de wrote:
 Hi,

 maybe a different approach could be to use a richer datatype than a function, 
 which carries both: the command and the undo command.

 (deftype Command [action undo])

 Then you could do something like:

 (defn do-patch!
   [command args]
   (dosync
     (let [patch {:command command :args (vec args)}]
       (apply (.action command) args)
       (alter patches- conj patch)
       patch)))

 (defn undo-patch!
   []
   (dosync
     ((.undo (:command patch)))
     (alter patches- pop)))

 Then you could provide specially crafted Commands and there would not be the 
 need to stub anything. Also Commands wouldn't have to be global objects.

 Sincerely
 Meikel

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: Mocking multimethods

2010-12-22 Thread Alyssa Kwan
The issue is where do I specify that:
(undo-fn ...patch...) = (fn [] (reset! visible-evidence-of-a-side-
effect :happened!))

undo-fn is a multimethod in my design, which requires a corresponding
defmethod for each patch type.  I need to create one for the scope of
the test, but defmethod by definition alters the top-level state of
the system, and doesn't automatically unroll like let or binding does.

On Dec 22, 6:33 am, Brian Marick mar...@exampler.com wrote:
 I think I misunderstand the issue, because this works for me:

 (ns midje.util.git
   (:use [midje.sweet]))

 ;; Code except for undo-patch is the same as before.
 ;; ...

 ;; I pulled remove-patch out of undo-patch because I was
 ;; getting a screwy read error I didn't want to figure out
 ;; at 5 in the morning, but I'd probably do that anyway:
 ;;http://codebetter.com/jeremymiller/2006/12/03/composed-method-pattern/

 (defn remove-patch [patch]
   (alter patches- #(remove (fn [p] (= patch p)) %)))

 (defn undo-patch [patch]
  (let [fn (undo-fn patch)]
    (dosync
      (fn)
      (remove-patch patch

 (fact The patch's undo-fn is called for its side effect and the patch is 
 forgotten
   (let [visible-evidence-of-a-side-effect (atom nil)]
     (undo-patch ...patch...) = anything
     (provided
       (undo-fn ...patch...) = (fn [] (reset! 
 visible-evidence-of-a-side-effect :happened!))
       (remove-patch ...patch...) = :nothing-of-interest)
     @visible-evidence-of-a-side-effect = :happened!))

 1855 $ lein midje midje.util.git
 All claimed facts (2) have been confirmed.
 1856 $

 -
 Brian Marick, Artisanal Labrador
 Contract programming in Ruby and Clojure
 Author of /Ring/ (forthcoming; 
 sample:http://bit.ly/hfdf9T)www.exampler.com,www.exampler.com/blog,www.twitter.com/marick

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: Mocking multimethods

2010-12-22 Thread Brian Marick

On Dec 22, 2010, at 6:52 AM, Alyssa Kwan wrote:

 The issue is where do I specify that:
 (undo-fn ...patch...) = (fn [] (reset! visible-evidence-of-a-side-
 effect :happened!))


The code you quoted is that specification. It doesn't matter that undo-fn is a 
multimethod.

Here's what the notation of the test says:

   When called with an arbitrary patch, undo-patch will produce a particular 
side effect. It does that because it uses undo-patch, which--when given that 
arbitrary patch--returns a function that produces that side effect. 
   It also calls remove-patch with the given patch.
   undo-patch can return anything it wants. We don't care.

 
 (fact The patch's undo-fn is called for its side effect and the patch is 
 forgotten
  (let [visible-evidence-of-a-side-effect (atom nil)]
(undo-patch ...patch...) = anything
(provided
  (undo-fn ...patch...) = (fn [] (reset! 
 visible-evidence-of-a-side-effect :happened!))
  (remove-patch ...patch...) = :nothing-of-interest)
@visible-evidence-of-a-side-effect = :happened!))


-
Brian Marick, Artisanal Labrador
Contract programming in Ruby and Clojure
Author of /Ring/ (forthcoming; sample: http://bit.ly/hfdf9T)
www.exampler.com, www.exampler.com/blog, www.twitter.com/marick

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: Mocking multimethods

2010-12-22 Thread Alyssa Kwan
Thanks, Brian!  I obviously didn't understand the nature of the
provided form.  That's really cool notation!  This is exactly what I
want.

What are the cons of using midje?  Any reason I shouldn't migrate all
my unit testing to it?

Thanks!
Alyssa

On Dec 22, 9:46 am, Brian Marick mar...@exampler.com wrote:
 On Dec 22, 2010, at 6:52 AM, Alyssa Kwan wrote:

  The issue is where do I specify that:
  (undo-fn ...patch...) = (fn [] (reset! visible-evidence-of-a-side-
  effect :happened!))

 The code you quoted is that specification. It doesn't matter that undo-fn is 
 a multimethod.

 Here's what the notation of the test says:

    When called with an arbitrary patch, undo-patch will produce a particular 
 side effect. It does that because it uses undo-patch, which--when given that 
 arbitrary patch--returns a function that produces that side effect.
    It also calls remove-patch with the given patch.
    undo-patch can return anything it wants. We don't care.



  (fact The patch's undo-fn is called for its side effect and the patch is 
  forgotten
   (let [visible-evidence-of-a-side-effect (atom nil)]
     (undo-patch ...patch...) = anything
     (provided
       (undo-fn ...patch...) = (fn [] (reset! 
  visible-evidence-of-a-side-effect :happened!))
       (remove-patch ...patch...) = :nothing-of-interest)
    �...@visible-evidence-of-a-side-effect = :happened!))

 -
 Brian Marick, Artisanal Labrador
 Contract programming in Ruby and Clojure
 Author of /Ring/ (forthcoming; 
 sample:http://bit.ly/hfdf9T)www.exampler.com,www.exampler.com/blog,www.twitter.com/marick

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: Mocking multimethods

2010-12-22 Thread Alex Baranosky
I love Midje and think migrating all the tests to it is a great idea.

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Re: Mocking multimethods

2010-12-22 Thread Brian Marick
 What are the cons of using midje?  Any reason I shouldn't migrate all
 my unit testing to it?


I'm biased, but I think Midje is ready for production use. There's a smooth 
migration path because Midje uses the same reporting as clojure.test, so you 
can mix styles in the same file and still use (for example) lein test to see if 
anything failed. (I'll go write up a Migrating page in the wiki after I send 
this mail.)

There are things clojure.test does that Midje doesn't:

* Named tests (deftest). So you can't write runner functions like this:

 (deftest arithmetic-tests
(subtraction-tests)
   (addition-tests))

  unless you also :use clojure.test and wrap the Midje facts in deftests (which 
works fine). 

* There's no equivalent to with-test.

* There's no #'are, though I suspect using checker functions would work as well.

Midje isn't a superset of the features of other clojure.test alternatives. For 
example, it doesn't have the auto-runner that LazyTest does, and it doesn't 
have the trimmed stack traces of Expectations. I plan to keep stealing ideas, 
though.

-
Brian Marick, Artisanal Labrador
Contract programming in Ruby and Clojure
Author of /Ring/ (forthcoming; sample: http://bit.ly/hfdf9T)
www.exampler.com, www.exampler.com/blog, www.twitter.com/marick

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: Mocking multimethods

2010-12-21 Thread Alex Baranosky
Hi Alyssa,

Using the midje library I was able to do your first test.  I'm pretty tired
so I this might be it for the night.

(fact throws an error if can't resolve undo function
  (undo-patch [2]) = (throws IllegalArgumentException No method in
multimethod 'undo-fn' for dispatch value: null))

Is this what you were thinking?

Alex

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Re: Mocking multimethods

2010-12-21 Thread Alex Baranosky
So I lied, I couldn't resist doing just one more:

(defn some-fn [] nil)

(fact calls the anonymous function that undo-fn returns
  (undo-patch ...patch...) = @patches-
  (provided
(undo-fn ...patch...) = some-fn
(some-fn) = nil))

The two provided statements are mboth mocking and stubbing.  So if either of
those functions isn't called with the expected arguments they will fail.
 The line
(undo-patch ...patch...) = @patches- is saying when I call undo-patch with
any patch, it should return the value inside of patches-  This is probably
ok, since the different undo-fns are thoroughly unit tested.

There might be some better way to not have to create that some-fn that we
just use for testing.  Maybe put it in a let or something?

Best,
Alex

P.S. Midje can be found on Github and Clojars.
https://github.com/marick/Midje

-- 
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en