Let's get the "Is TDD dead?" panel to talk about "mocks returning mocks returning mocks"

121 views
Skip to first unread message

Philip Schwarz

unread,
Jun 1, 2014, 2:00:23 AM6/1/14
to software_cr...@googlegroups.com
The panel (@martinFowler @KentBeck and @dhh) of the "Is TDD dead?" Google Hangout https://plus.google.com/events/cco30ri6dpkej4h4d8mejmat98o have invited spectators to put forward questions for the final part of the debate. 

IMHO It would be a shame if we missed the opportunity to ask them to talk about "mocks returning mocks returning mocks" and the important points so effectively described by @garybernhardt in "Test Isolation Is About Avoiding Mocks" https://www.destroyallsoftware.com/blog/2014/test-isolation-is-about-avoiding-mocks

If the topic matters to you, how about putting forward your questions, so there is a better chance of the topic being debated? 

To propose a question, just click play on the video you see on the hangout page.

Philip Schwarz

Philip Schwarz

unread,
Jun 1, 2014, 3:30:09 AM6/1/14
to software_cr...@googlegroups.com
First request:

+Kent Beck +Martin Fowler Please, please, follow up on the following:

@KentBeck TIL what "tell, don't ask" really means. horrified.
@martinfowler @kentbeck I realize I forgot to ask you about that during the hangout :-(

Philip Schwarz

unread,
Jun 1, 2014, 3:32:47 AM6/1/14
to software_cr...@googlegroups.com
Second Question:

+Kent Beck  I would really like to know how your view of the "Law of Demeter" compares to the following:

+David Heinemeier Hannson  "pseudoscience" [1]
+Martin Fowler  "stepping stone towards co-locating behavior and data...not worth highlighting" [2]
+Nat Pryce  : "OO code that follows LoD is easier to understand and maintain" [3]
+Allen Holub  "the primary directive of OO systems" [4]

[1] RailsConf 2014 - Keynote: Writing Software by David Heinemeier Hansson
[2] http://martinfowler.com/bliki/TellDontAsk.html
[3] http://www.mockobjects.com/2006/10/tell-dont-ask-and-mock-objects.html
[4] http://tinyurl.com/ml3oss4 + http://www.drdobbs.com/testing/testing-oo-systems-part-1/240000411 


On Sunday, 1 June 2014 07:00:23 UTC+1, Philip Schwarz wrote:

Philip Schwarz

unread,
Jun 1, 2014, 3:34:52 AM6/1/14
to software_cr...@googlegroups.com
Third Question:

+Kent Beck  +Martin Fowler +David Heinemeier Hannson Can you please discuss the point made by +Gary Bernhardt on your following exchange in part 1 of this hangout:

+Kent Beck : "I just don't go very far down the mock path. I look at code where you have mock returning mocks returning mock and my experience is if I have, if I use TDD I can refactor stuff, and then I heard these stories, people say "well I use TDD and now I can't refactor anything" and like I could not understand that, and then I started looking at their tests and well, if you have mocks returning mocks returning mocks, your test is completely coupled to the implementation, not the interface, but the exact implementation of some object, you know, three streets away, of course you can't change anything without breaking the test. So that for me is too high a price to pay.That is not a trade off I am willing to make just to get piecemeal development
+Martin Fowler: "And this is I think much at the heart of this. Confusion about over terminology And what these different things are. When I read David's initial blog post...one of the things that came through very clearly was his criticism of TDD and of how the design damage that comes/flows through it has in itself tied in a notion of the strong desire for isolation and mocking. And it is very important to point out that there is nothing within the idea of how you do either TDD or unit testing that says you have to have that kind of isolation. Some people are very much in favour of it, others aren't...Our style of testing, we don't bother with isolation and you know, it is working very well for us, thank you very much. So that is one thing, wheter TDD and Unit testing should be tied in with the idea of isolation, and I look at it as different schools of thought, and I am with Kent, I hardly ever use mocks, but I know good people who do, so I don't want to shoot everybody who uses mocks, maybe give it 10 more years and then we'll drum them out or something, we'll see."  

+Gary Bernhardt  wrote a great blog post called "Test Isolation Is About Avoiding Mocks" https://www.destroyallsoftware.com/blog/2014/test-isolation-is-about-avoiding-mocks.

+Kent Beck  +Martin Fowler  +David Heinemeier Hannson  Can you discuss the point he makes in the post?
Here are the post's concluding thoughts:

"This post was triggered by Kent's comment about triply-nested mocks. I doubt that he intended to claim that mocking three levels deep is inherent to, or even common in, isolated testing. However, many others have proposed exactly that straw man argument. That argument misrepresents isolated testing to discredit it; it presents deep mocks, which are to be avoided in isolated testing, as being fundamental to it; it's fallacious. It's at the root of the claim that mocking inherently makes tests fragile and refactoring difficult. That's very true of deep mocks, but not very true of mock-based isolation done well, and certainly isn't true of isolation done without mocks.

In a very real sense, isolated testing done test-first exposes design mistakes before they're made. It translates coupling distributed throughout the module into mock setup centralized in the test, and it does that before the coupling is even written down. With practice, that preemptive design feedback becomes internalized in the programmer, granting some of the benefit even when not writing tests. There may be other paths to that skill, but I'm still learning from my tests after seven years of isolating around 50% of the time. This path also happens to produce a trail of sub-millisecond tests fully covering every component designed using it, which is alright with me."


On Sunday, 1 June 2014 07:00:23 UTC+1, Philip Schwarz wrote:

Matteo Vaccari

unread,
Jun 1, 2014, 5:01:55 AM6/1/14
to software_cr...@googlegroups.com

IMHO It would be a shame if we missed the opportunity to ask them to talk about "mocks returning mocks returning mocks" and the important points so effectively described by @garybernhardt in "Test Isolation Is About Avoiding Mocks" https://www.destroyallsoftware.com/blog/2014/test-isolation-is-about-avoiding-mocks



It seems to me that the post by Gary Bernhardt is very much concerned about "how do I test drive ActiveRecord objects".  Most of the controversy seems to be related to this fact; if you follow dependency inversion, GOOS-style, repositories, ports-and-adapters and the like, you would never work with an object that reflects the database schema directly.  The whole problem disappears.  

On the other hand, if you do want to use Rails' ActiveRecord, you will bind you objects very tightly to the schema.  That's a design choice; it makes some things easier and some other things harder.  

What I want to say is this: if someone gives for granted that he does /not/ want his models to depend directly on the database; and someone else gives for granted that he wants to use ActiveRecord, then when they evaluate their results with TDD it's quite likely that they will experience very different results.  At the very least, the kind of difficulties they encounter will be vastly different.   When they discuss, they would probably find it more productive to start the discussion from this different worldview, rather than from TDD itself.

Matteo

Ron Jeffries

unread,
Jun 1, 2014, 5:23:00 AM6/1/14
to software_cr...@googlegroups.com
Matteo,

On Jun 1, 2014, at 5:01 AM, Matteo Vaccari <matteo....@gmail.com> wrote:

What I want to say is this: if someone gives for granted that he does /not/ want his models to depend directly on the database; and someone else gives for granted that he wants to use ActiveRecord, then when they evaluate their results with TDD it's quite likely that they will experience very different results.  At the very least, the kind of difficulties they encounter will be vastly different.   When they discuss, they would probably find it more productive to start the discussion from this different worldview, rather than from TDD itself.

Yes, very much yes. I have personally found ActiveRecord to be hard to TDD with and in fact hard to work with in the style I prefer, which is to have my model disconnected from the database. 

We have seen other difference of view between Kent and DHH, notably that DHH doesn’t care as much about low coupling and high cohesion as Kent does. This preference might impact how well TDD would work — I’m not sure. It would certainly impact where you’d wind up.

My reading of the videos so far is that DHH has never experienced TDD as Kent defined it. He’s not the only one: it turns out that you have to let go of some beliefs and let TDD happen.

I think the only possibility of real agreement between Kent and DHH would be if they were to pair program something significant. That would be interesting but it seems unlikely.
Impossible is not a fact. It is an opinion.  -- Muhammad Ali


Philip Schwarz

unread,
Jun 1, 2014, 3:31:34 PM6/1/14
to software_cr...@googlegroups.com
Hi Matteo,

thanks for your input.

>It seems to me that the post by Gary Bernhardt is very much concerned about "how do I test drive ActiveRecord objects"
I believe he is making a more general point. 

Here is what he tweeted the day he wrote the blog post:

@garybernhardt That post is the first time that I've ever managed to directly explain the design pressure of isolation in detail. It only took seven years.

 And in his Boundaries talk (2012) he makes a similar case for the proper use of isolation but without mentioning ActiveRecord, e.g.:

...What we have here is a three class system. 

 
These three classes integrate in production, but in test we are removing two of the dependencies, replacing them with stubs and mocks, giving us this as our testing world.
So  everything is nice and isolated.
There are several good reasons to do this. Several very big benefits that come out of it, but there is also one really terrible thing that happens when you do this.
So let's go through those.
(1) This allows you to do real test-driven design: looking at your tests, seeing that you have mocked six things and two of them are mocked three levels deep, this tells you that your design is not so good for this class.
So it gives you a form of feedback that you can't get without isolated tests. At least I don't know how to. 
(2) It allows you to do outside in TDD, where you actually build the high-level pieces before the low level pieces exist. So we could TDD the sweeper using the User, using the UserMailer, before those classes exist, because we are just stubbing them out anyway. Then when we want to write the user class for real, we can look at what we stubbed and that tells us the interfaces it needs.
(3) And finally, this gives you very fast tests, it is one of the main things in the whole fast Rails tests 'meme', or, I don't want to call it a movement, people getting really excited about fast tests in the Rails world.
And we are talking about the difference between a 200 ms time from hitting return to seeing the prompt back, versus a 30 second time to run a very small test. It is a very big difference when you are really isolating.
So these are all very good things that you want, but they are balanced out by a very bad thing, and that bad thing is that in test, you are running against a stub and a mock, and in production you are running against real classes, and if you don't stub the boundaries correctly, you rtests will pass and the production system will be wrong. And this is such a big problem that for most people I think overshadows all those benefits. Even if you explain them to them , they are going to look at hits problem and say it is not worth it.
Now, there have been attempts to fix this. Various approaches to solve this problem in one way or another
...
...
...
So that is all background, this is how I came to the ideas that I am going to talk about for the rest of this talk. This has been a large focus of my s/w development career for the last five years is, isolated testing and thinking about how to do it well...

Philip


Matteo Vaccari

unread,
Jun 2, 2014, 3:52:06 AM6/2/14
to software_cr...@googlegroups.com
On Sun, Jun 1, 2014 at 9:31 PM, Philip Schwarz <philip.joh...@googlemail.com> wrote:
Hi Matteo,

thanks for your input.

>It seems to me that the post by Gary Bernhardt is very much concerned about "how do I test drive ActiveRecord objects"
I believe he is making a more general point. 

Here is what he tweeted the day he wrote the blog post:

@garybernhardt That post is the first time that I've ever managed to directly explain the design pressure of isolation in detail. It only took seven years.


I'll have to read it more carefully then.  Still, my impression is that his thinking is heavily influenced by the constraint of wanting to use ActiveRecord.  

He writes:

def average_transaction_amount(user)
  purchases = user.purchases
  total = purchases.map(&:transaction).map(&:amount).inject(&:+)
  total / purchases.count
end

This method has deep knowledge about the database's structure

When I read that method, I think that the method has deep knowledge about the /objects/ structure.  The fact that it also has deep knowledge about the DB is a consequence of AR.  The overarching theme of his piece is "isolation", by which he means "isolation from the DB".  He spends a lot of energy to undo AR's primary strength, that is, having objects that are automatic, transparent reflections of the DB schema.

Traditional, repository-style persistence separates cleanly business logic from mapping objects-to-DB.  That way you can simply say

user = User.new
user.add_purchase Purchase.new(amount: 100)
user.add_purchase Purchase.new(amount: 200)
assert_equal 150, user.average_transaction_amount

I mean that in the repository way, it's much more likely that you will use the real Purchase object, because when you're testing business logic, you don't have to worry about the DB implications of adding an element to a one-to-many relation.   The price you pay is that it will probably take some work to write the repository. 

Your primary concern becomes finding the right objects, finding the right abstractions to express your domain logic in a clear and concise way; not isolation.  You also have a concern of how to persist that domain logic to the database, but you don't have to think about these two concerns at the same time all the time, like you do with AR.

Matteo




Fabio Armani

unread,
Jun 2, 2014, 9:00:29 AM6/2/14
to software_cr...@googlegroups.com
I agree with Matteo, the focus of the discussion is more on the Different paradigma than on directly on TDD.

fA

Sent from Fabio's iPhone
--
You received this message because you are subscribed to the Google Groups "software_craftsmanship" group.
To unsubscribe from this group and stop receiving emails from it, send an email to software_craftsma...@googlegroups.com.
To post to this group, send email to software_cr...@googlegroups.com.
Visit this group at http://groups.google.com/group/software_craftsmanship.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages