[Esug-list] Proposal for Mock Objects at ESUG 2011
chaetal at gmail.com
Tue May 31 16:02:58 EDT 2011
First of all, I'm sorry for my English. (And I'll appreciate it very much if
someone will point to mistakes of any kind in the following text.)
I attempted to analyze and structure comments, questions and ideas presented
in the thread and I think I found one thing that can be a starting point for
the discussion (despite the fact the discussion already started… away back).
I've got a strong feeling (I've got it several years ago and had a number of
evidences, including several in this thread) that there are (at least) two
very different (if not opposite) points of view on mock objects… and it
seems to me, the root of this divergence is even deeper and more general —
understanding of TDD. So, I think I have to explain my point of view.
(Telling "my" here I claim to be an author of all errors, misinterpretations
and nonsenses I'll present. But all the good, reasonable and interesting
things were invented by other (clever) people.)
So, why and how did I dare to claim TDD is not really popular among
Smalltalkers? Because I still believe tests can drive (nearly) all stages of
development: not just coding, but also design and even analysis (at least to
clarify and formalize results of analysis). But I rarely (if ever) see any
artifacts (tests of the kind). Perhaps I'm wrong, but most of the tests I
saw were more "acceptance" tests, perhaps even written afterwards. Once
again: very likely I'm wrong (and I'll be glad to be, actually).
Why is it important for me to use TDD? Because, as a bad programmer, I need
a help. I have a lot of things I must think about during development (won't
enumerate, we all know them), so I want a tool that helps me not to think
too much — at least, at the points where it is possible at all. (If I
misinterpret the word "driving" here, please let me know.)
Thus, if I write a line of code that is not directly(!) driven by a test, I
don't TDD. If I make some up-front design I'm not actually test-driven.
Even, if I simply go few steps designing top-down without tests at a very
small single stage of development, TDD is broken. If you think it is too
extreme and strict — ok, let's talk about "seamless TDD" vs., say, "partial
TDD". (Yes, I know about steps of variable size during TDD etc. But I still
think it's a bit another thing. Let's judge a bit later…)
So, considering an example by Colin Putney:
| set |
set := Set new.
set add: 3.
self assert: (set includes: 3).
| set |
mock := MockArray new: 5
mock expect: (Message selector: #at:put: arguments: #(4 3)).
set := Set withArray: mock.
set add: 3.
Isn't it obvious mocking array here is an overkill and the first test is
better? For sure it is.
But why? Because there's actually no development in the 1st test. Otherwise,
what is it for: #add: or #includes:? Which feature this test makes me to add
to the system-under-test (the Set)? I must select, I must think, thus I'm
not driven by the test.
Consider also (slightly) another issue: here we have a ready-to-use inner
collection for the Set. But if I have no yet? Isn't it a common and very
frequent situation during development: I have to implement a (sub)system
that comprises several objects working together? (I think I don't have to
provide examples here to prove.) Which one should I implement first? I don't
know, I have to explore. I must analyze the system, i.e. divide it into
subcomponents, find the ones independent and start by implementing them
But what is this process, I've just described? It's a top-down up-front
design. Is it test-driven? Obviously, no. Do I have an alternative? Yes, I
can use mocks. With Mocketry I could write:
SetTests >> testStoresElementsInInnerCollection
set := Set withArray: collection.
[set add: #element] should strictly satisfy: [collection add: #element]]
Of course, this is a very early step of development, as I'll have to
implement an efficient algorithm to check, if element exists in the set
before adding it… And with mocks it is very likely I'll be driven to
implement it as a pluggable object. Probably, this can make Set a bit more
flexible, doesn't it? On the other hand, it can introduce unnecessary
complexity. Apparently, this is a good example to explore it further… While
I must admit technique of mock object is not a silver bullet, and must be
used carefully… just as any other tool.
Yes, mock objects is a specific tool for specific situations. But as for me,
I meet these specific situation almost as frequently, as other,
I think mocks should be used, first of all, to discover different aspects of
functionality and distribute it among various objects. (Documentation,
optimization, etc. goes later… if goes at all…)
When I start to work on a new feature, I first try to write an acceptance
test. But I never can implement it 'as is'. And I hate to leave it red or
mark it as ignored (in any way). So I try to transform it into something I
can implement easily and fast. And often this leads me to a test with mocks.
This make me to divide complex functionality into smaller blocks and factor
them out to collaborators. The trick is to make those objects to behave
(one-by-one and altogether)… But that's another (and big) topic.
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Esug-list