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

FS
Frank Shearar
Fri, May 27, 2011 9:34 PM

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 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 >
CP
Colin Putney
Fri, May 27, 2011 9:44 PM

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 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
FS
Frank Shearar
Fri, May 27, 2011 9:50 PM

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:

http://source.wiresong.ca/mc/MockLibrary-cwp.7.mcz

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

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: > > http://source.wiresong.ca/mc/MockLibrary-cwp.7.mcz 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
HW
Hernan Wilkinson
Fri, May 27, 2011 10:37 PM

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/

> > > 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/>*
DS
Dennis Schetinin
Sat, May 28, 2011 4:43 AM

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

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