Re: [Esug-list] Fwd: Re: Proposal for Mock Objects at ESUG 2011

On 27 May 2011 21:56, Colin Putney <colin@wiresong.com> wrote:
On Fri, May 27, 2011 at 12:42 PM, Frank Shearar <frank.shearar@gmail.com> wrote:
You miss the point of mocks. Mocks are there to allow you to test your code quickly and cheaply against things that may not work in a quick manner.
Not likely. Hernán has been around the block and he speaks from long experience and deep thinking on software design. If you read his post a little more carefully, you'll see that he distinguishes between test doubles in general and mocks as a particular form of test double. He uses test doubles - you don't get 23,000 tests to run in 7 minutes without them. It's just *mocks* that are mostly unnecessary.
Hernan, I didn't mean to bite your head off. Next time I'll pause a bit longer.
Personally, I find that mocks are *very* useful in a few very specific situations, but they can cause problems it other situations. They're like a very specialized tool - invaluable when you need it, but kept in the bottom of the toolbox and not used very often. Where I find them useful is in testing communication. For example, I'll use a MockWritestream to throw an exception if the code under test writes something unexpected into the stream. It's handy when testing code that writes out files in a particular format, or communicates with remote systems over the network. It might also be useful to test communications across formal module boundaries, where it's particularly important that a specific protocol be used - eg, for something like Prevalayer.
Indeed, and in these cases and the other appropriate use cases I'd like to see a nicely written, widely used, standardised framework precisely so we (I) don't have to write our (my) own. Anyhow, talk's cheap, and once I've finished my current chunk of work I'll try beat SSpec into shape. frank
My favourite testing pattern is TrivialImplementation. Instead of using a test double or an expensive "real" object, I often create a cheap/trivial implementation of the objects that collaborate with the objects I'm testing. For example, Monticello2 has several repository classes. There are implementations that store code in a single file, in a directory of files, on a remote machine via sockets, on a web server via HTTP etc. There's also a trivial implementation that just uses an in-memory Dictionary for storage. It's a complete implementation of the repository protocol, and Monticello can use it "for real", but it's not as robust as the other kinds of repository. All the repository implementations, including MemoryRepository, have a suite of tests that ensure that they work correctly. But when I'm testing other parts of Monticello that interact with a repository, those tests use a MemoryRepository.
I know that BDD folks like to talk about testing behaviour rather than state, but I don't find the distinction useful. Testing state breaks the encapsulation of the objects under test, and couples the test too closely to their internal implementation. Testing behaviour using mocks *does the same thing*; it just restricts the implementation in a different way. I find it's better to give the implementation a degree of freedom by testing "results". Figuring out what result you're looking for can be difficult - it requires thinking about what a passing test really tells you.
Here's an example - let's say we we're testing an implementation of Set. Here's the version that tests state:
| set | set := Set new. set add: 3. self assert: (set instVarNamed: 'array') = #(nil nil nil 3 nil)
Here we test behaviour:
| set | mock := MockArray new: 5 mock expect: (Message selector: #at:put: arguments: #(4 3)). set := Set withArray: mock. set add: 3.
I prefer this approach. It gives the implementation a lot of freedom, while ensuring that it does what we really want:
| set | set := Set new. set add: 3. self assert: (set includes: 3).
In short, I agree with Hernán. Mocks can be useful, but they're often overused. In most cases, they're unnecessary.
Colin

On Fri, May 27, 2011 at 2:34 PM, Frank Shearar <frank.shearar@gmail.com> wrote:
Indeed, and in these cases and the other appropriate use cases I'd like to see a nicely written, widely used, standardised framework precisely so we (I) don't have to write our (my) own.
FWIW, here's the mock library I use - I think a framework is overkill: http://source.wiresong.ca/mc/MockLibrary-cwp.7.mcz Colin

On 27 May 2011 22:44, Colin Putney <colin@wiresong.com> wrote:
On Fri, May 27, 2011 at 2:34 PM, Frank Shearar <frank.shearar@gmail.com> wrote:
Indeed, and in these cases and the other appropriate use cases I'd like to see a nicely written, widely used, standardised framework precisely so we (I) don't have to write our (my) own.
FWIW, here's the mock library I use - I think a framework is overkill:
Cool, I'll take a look. I suppose I could say "library" or "package" or whatever instead of "framework". It could be as simple as one or two classes. frank

Hernan, I didn't mean to bite your head off. Next time I'll pause a bit longer.
don't worry! It is very common to get passionated about these things and I like that :-), it means we care about them. I'd like to see the talk at Esug. Bye, Hernan.
Personally, I find that mocks are *very* useful in a few very specific situations, but they can cause problems it other situations. They're like a very specialized tool - invaluable when you need it, but kept in the bottom of the toolbox and not used very often. Where I find them useful is in testing communication. For example, I'll use a MockWritestream to throw an exception if the code under test writes something unexpected into the stream. It's handy when testing code that writes out files in a particular format, or communicates with remote systems over the network. It might also be useful to test communications across formal module boundaries, where it's particularly important that a specific protocol be used - eg, for something like Prevalayer.
Indeed, and in these cases and the other appropriate use cases I'd like to see a nicely written, widely used, standardised framework precisely so we (I) don't have to write our (my) own.
Anyhow, talk's cheap, and once I've finished my current chunk of work I'll try beat SSpec into shape.
frank
My favourite testing pattern is TrivialImplementation. Instead of using a test double or an expensive "real" object, I often create a cheap/trivial implementation of the objects that collaborate with the objects I'm testing. For example, Monticello2 has several repository classes. There are implementations that store code in a single file, in a directory of files, on a remote machine via sockets, on a web server via HTTP etc. There's also a trivial implementation that just uses an in-memory Dictionary for storage. It's a complete implementation of the repository protocol, and Monticello can use it "for real", but it's not as robust as the other kinds of repository. All the repository implementations, including MemoryRepository, have a suite of tests that ensure that they work correctly. But when I'm testing other parts of Monticello that interact with a repository, those tests use a MemoryRepository.
I know that BDD folks like to talk about testing behaviour rather than state, but I don't find the distinction useful. Testing state breaks the encapsulation of the objects under test, and couples the test too closely to their internal implementation. Testing behaviour using mocks *does the same thing*; it just restricts the implementation in a different way. I find it's better to give the implementation a degree of freedom by testing "results". Figuring out what result you're looking for can be difficult - it requires thinking about what a passing test really tells you.
Here's an example - let's say we we're testing an implementation of Set. Here's the version that tests state:
| set | set := Set new. set add: 3. self assert: (set instVarNamed: 'array') = #(nil nil nil 3 nil)
Here we test behaviour:
| set | mock := MockArray new: 5 mock expect: (Message selector: #at:put: arguments: #(4 3)). set := Set withArray: mock. set add: 3.
I prefer this approach. It gives the implementation a lot of freedom, while ensuring that it does what we really want:
| set | set := Set new. set add: 3. self assert: (set includes: 3).
In short, I agree with Hernán. Mocks can be useful, but they're often overused. In most cases, they're unnecessary.
Colin
-- *Hernán Wilkinson Agile Software Development, Teaching & Coaching Mobile: +54 - 911 - 4470 - 7207 email: hernan.wilkinson@10Pines.com site: http://www.10Pines.com <http://www.10pines.com/>*

Thank you very much for all the replies, comments, criticism, ideas, references… inspiration! I really didn't expect such an interesting discussion to go. And now I clearly see the topic is questionable and worth discussion. I'll for sure explain my point further here and hopefully at conference, just have to think and structure my thoughts before a bit. Once again, thank you! :) -- Dennis Schetinin
participants (4)
-
Colin Putney
-
Dennis Schetinin
-
Frank Shearar
-
Hernan Wilkinson