+1 - I think we are saying the same thing (not sure if you meant to
reply to me?)

On 6 January 2015 at 14:35, Timothy Baldridge <tbaldri...@gmail.com> wrote:
> I think the answer to questions like this is that you are testing the wrong
> thing, or more correctly you are writing incomplete tests.
>
> In your example, you stubbed out check-pw. But calling check-pw has a
> contract, a contract that (at the moment) only exists in your head, but a
> contract none-the-less. That contract needs to be tested, from both sides.
> Tests should invoke all instances of check-pw. In addition you should have a
> test that pairs a login form with a check-pw and runs tests against this
> system.
>
> Some people call these tests "integration tests" or "system tests". But I
> think of them as contract tests.
>
> Here's a diagram of the problem:
>
> login -----> check-pw
>
> I've found that most code that uses mocking will test the login and the
> check-pw bits, but completely neglect testing the "arrow" between them, and
> when that happens, you get exactly the experience you described.
>
> The other thing I'd like to mention is that I have found it very valuable to
> sit down and think about what code is actually being hit by a test. In your
> example, if check-pw and the db are both mocked, what is actually being
> tested? In your example all you are testing with those functions mocked is
> that Clojure is capable of compiling a function that calls two other
> functions. I can't tell you how many times I've looked at mocked tests and
> realized that the only thing being tested is something like read-string,
> get, or destructuring.
>
> So my personal approach is this: write very coarse tests that exercise the
> entire system. These will catch the protocol mis-matches. Then if you want
> more detail for when tests do fail, write more specific tests. In short:
>
> System (integration) tests: so I feel good about my codebase
> Unit (smaller) tests: so I can figure out what went wrong when the larger
> tests fail.
>
> Timothy
>
> On Tue, Jan 6, 2015 at 6:26 AM, Colin Yates <colin.ya...@gmail.com> wrote:
>>
>> I don't think there is an easy answer here, and note that this is a
>> problem generic to mocking (i.e. not clojure or midje specific).
>>
>> The usual advice applies though:
>>  - do you really need to mock? Unit testing is about the coarseness of
>> granularity which is defined more by cohesion and abstractions than "one
>> function and only this function" (e.g. remove the problem by not overly
>> mocking)
>>  - make everything fail then fix rather than fix and then upgrade (i.e.
>> update every instance of the call/mock to check-pw before the implementation
>> of check-pw).
>>  - Clojure's lack of types means the compiler can't help. Schema or
>> core.typed can. This isn't *the* answer, but I have found it very helpful.
>>
>> As mentioned elsewhere, mocking in general is a very powerful tool, but it
>> is does need wielding carefully. These problems are easier to swallow in
>> strongly typed languages because of IDE support (changing parameters around
>> in Java with IntelliJ is a matter of a few key presses for example).
>>
>> Hope this helps.
>>
>>
>> On Tuesday, 6 January 2015 08:22:36 UTC, Akos Gyimesi wrote:
>>>
>>>
>>> On Sat, Jan 3, 2015, at 02:46 AM, Brian Marick wrote:
>>> >
>>> > > I use TDD and mocking/stubbing (conjure) to test each layer of my
>>> > > code.
>>> > > The problem is when I change the function signature and the tests do
>>> > > not
>>> > > break, because the mocks/stubs do not know when their argument lists
>>> > > no
>>> > > longer agree with the underlying function they are mocking.  Is there
>>> > > a
>>> > > way to catch this?  Short of a test suite that eschews stubbing in
>>> > > favor
>>> > > of full setup/teardown of DB data for each test?
>>> >
>>> > Could you give an example? I use mocks fairly heavily, and I don't seem
>>> > to have this problem. Perhaps it's because I change the tests before
>>> > the
>>> > code?
>>>
>>> Although the subject changed a little bit, I would be still interested
>>> in your approach to refactoring if there is heavy use of mocking. Let me
>>> give you an example:
>>>
>>> Let's say I am writing a login form, trying to use the top-down approach
>>> you described. My approach could be the following:
>>>
>>> (unfinished check-pw)
>>>
>>> (fact "login-form succeeds if user enters the correct password"
>>>   (login-form-success? {:username "admin" :password "secret"}) => true
>>>   (provided
>>>     (db/get-user "admin") => (contains (:password "my-secret-hash"))
>>>     (check-pw "my-secret-hash" "secret") => true))
>>>
>>> (defn login-form-success? [user-input]
>>>   (let [user (db/get-user (:username user-input))]
>>>     (check-pw (:password user) (:password user-input))))
>>>
>>> Then I finish the check-pw function and everything works.
>>>
>>> Now, later that day I decide that I pass the whole user object to the
>>> check-pw function. Maybe I want to use the user ID as a salt, or maybe I
>>> just want to leave the possibility for checking password expiration,
>>> etc. So I modify the test and the implementation of check-pw so that the
>>> first parameter is the user object, not the password hash.
>>>
>>> Suddenly my co-worker comes to me saying "hey, I need you on a meeting
>>> right now!" I close my laptop, and an hour later I think "where were
>>> we?..." I run all the tests, and they all pass, so I commit.
>>>
>>> Except... I forgot to modify all the invocations of check-pw in both the
>>> test and the implementation. Every test pass, so I have no way of
>>> finding out the problem without careful code review or by examining the
>>> stack traces from the live code.
>>>
>>> While this bug is easy to catch, what if my function is mocked in
>>> several places, and I fail to rewrite all of them properly?
>>>
>>> Do you have any advice on what you would have done differently here to
>>> avoid this bug?
>>>
>>> Regards,
>>> Akos
>>
>> --
>> 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
>> ---
>> You received this message because you are subscribed to the Google Groups
>> "Clojure" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to clojure+unsubscr...@googlegroups.com.
>> For more options, visit https://groups.google.com/d/optout.
>
>
>
>
> --
> “One of the main causes of the fall of the Roman Empire was that–lacking
> zero–they had no way to indicate successful termination of their C
> programs.”
> (Robert Firth)
>
> --
> 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
> ---
> You received this message because you are subscribed to a topic in the
> Google Groups "Clojure" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/clojure/T8fIW27kDYE/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to