I've just finished reading the article on mock objects at
http://www.mockobjects.com/files/endotesting.pdf. There was one
section I was confused with:
***************************************************************
Thirdly, developing with Mock Objects tends to push behaviour towards
Visitor-like objects
[Gamma 1994] that are passed around; we call these Smart Handlers. For
example, rather
than having code that queries attributes from an object and writes
each one to a stream, a first
step would be to pass a stream to the object which then writes out its
attributes. This
preserves the encapsulation of the object. Thus, the code changes
from:
public void printPersonReport(Person person, PrintWriter writer) {
writer.println(person.getName());
writer.println(person.getAge());
writer.println(person.getTelephone());
}
to:
public void printPersonReport(Person person, PrintWriter writer) {
person.printDetails(writer);
}
public class Person {
public void printDetails(PrintWriter writer) {
writer.println(myName);
writer.println(myAge);
writer.println(myTelephone);
}
...
}
As this code becomes more complex however, it becomes difficult to
test cleanly because the
generic println method used in printDetails loses information about
our understanding of the
domain. Instead, we can write a handler object to reify this dialogue
between a stream and a
Person:
public void handleDetails(PersonHandler handler) {
handler.name(myName);
handler.age(myAge);
handler.telephone(myTelephone);
}
This separates the input and output aspects of rendering a Person on a
stream. We can test
both that we have the inputs that we expect, and that a given set of
values is rendered
correctly. The unit test for the handler inputs would then be:
void testPersonHandling() {
myMockHandler.setExpectedName(NAME);
myMockHandler.setExpectedAge(AGE);
myMockHandler.setExpectedTelephone(TELEPHONE);
myPerson.handleDetails(myMockHandler);
myMockHandler.verify();
}
followed by a separate unit test to check that the domain code for
PersonHandler outputs
itself correctly:
void testPersonHandler() {
myMockPrintWriter.setExpectedOutputPattern(
".*" + NAME + ".*" + AGE + ".*" + TELEPHONE + ".*");
myHandler.name(NAME);
myHandler.age(AGE);
myHandler.telephone(TELEPHONE);
myHandler.writeTo(myMockPrintWriter);
myMockPrintWriter.verify();
}
These three effects mean that code developed with Mock Objects tends
to conform to the Law
of Demeter [Lieberherr 1989], as an emergent property. The unit tests
push us towards
writing domain code that refers only to local objects and parameters,
without an explicit policy
to do so.
***************************************************************
Now, I understand the first switch. The very first example requires
printPersonReport to know about the interface of person.. this is
removed in the second example.
However, I am unsure of the switch to the handler:
"As this code becomes more complex however, it becomes difficult to
test cleanly because the generic println method used in printDetails
loses information about our understanding of the domain. Instead, we
can write a handler object to reify this dialogue between a stream and
a Person:"
I don't really understand what is being said. Can anyone shed some
light on this?
Cheers
Taras
> I've just finished reading the article on mock objects at
> http://www.mockobjects.com/files/endotesting.pdf. There was one
> section I was confused with:
> ***************************************************************
> Thirdly, developing with Mock Objects tends to push behaviour towards
> Visitor-like objects
> [Gamma 1994] that are passed around; we call these Smart Handlers.
The Visitor pattern is used in OO prog langs that are difficient wrt
supporting the capability known as "multiple dispatch" .
The example does not exhibit the characteristics of multiple dispatch,
so the claim is incorrect.
If anything, the "Smart Handler" is more akin to the Strategy pattern.
The Demeter principles are applied in the example *before* any mention
of a test stub.
> Now, I understand the first switch. The very first example requires
> printPersonReport to know about the interface of person.. this is
> removed in the second example.
> However, I am unsure of the switch to the handler:
> "As this code becomes more complex however, it becomes difficult to
> test cleanly because the generic println method used in printDetails
> loses information about our understanding of the domain. Instead, we
> can write a handler object to reify this dialogue between a stream and
> a Person:"
> I don't really understand what is being said. Can anyone shed some
> light on this?
All the claims made for the given example are fallacy.
Which is why you are probably having trouble understanding.
Regards,
Steven Perryman
This is a Hot Button for me because I see huge amounts of testing effort
wasted because the application is poorly formed in an OO sense...
> The unit tests push us towards
> writing domain code that refers only to local objects and parameters,
> without an explicit policy
> to do so.
Obviously they aren't doing a very good job of pushing. This is an
example of what happens when one *doesn't* do DFT. Look at what is
happening here. The customized code for the mock objects and their
infrastructure is probably going to be integer factors more that the
code in the UUT itself! (It may be so repetitive compared to other UUT
tests that the tester can write it while drunk, but there shouldn't be
any need to write it in the first place.)
Any time one has to resort to something this complex and customized to
unit test a specific UUT there is something seriously wrong with the
problem space abstraction of the UUT itself because it is bleeding
cohesion all over the application. Fix that and you won't need mock
objects, much less this sort of convoluted supporting infrastructure.
<aside>
FYI, the commercial translation IDEs usually provide a generic test
harness OTB that can be used for unit testing. All the developer
provides is specific test case data for the UUT in a text file. That's
possible because the translation methodologies have to support full code
generators for OOA models. Full code generators are only viable if the
OOA model was constructed in a very disciplined OO fashion where methods
are self-contained, cohesive, and properly encapsulated while
collaborations separate message from method in an asynchronous
communication model. Thus there are never any behavioral dependencies
between methods so testing benefits because one doesn't need customized
mock objects to emulate such dependencies.
</aside>
--
There is nothing wrong with me that could
not be cured by a capful of Drano.
H. S. Lahman
h...@pathfindermda.com
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
in...@pathfindermda.com for your copy.
Pathfinder is hiring:
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
I suspect that unit testing - especially test-first - forces the dependencies
between objects to grow very narrow. Test-free code might possibly indulge in
multiple channels between objects, because programmers have no incentive
(besides a vague sense of guilt) to shrink these channels down.
Expensive setup is a design smell. That means if you need to create too many
objects just to test one of them, your objects are too coupled. Test cases
should construct the fewest possible objects before testing one of them. So
simple laziness in the tests translates into a direct force to decouple your
code objects.
But now we come to mock objects. I'm aware that a blog with "mockobjects" in its
URI should be expected to indulge in promotion. But, in my own code, I only mock
to avoid an expensive side-effect in lower-level code. I do not mock to make
expensive setup easier, because that would only perfume the code smell. Mocks
might possibly make objects easier to couple.
So I'm curious (but I don't know enough Java to verify) if these guys could have
used TDD to emerge a good design - a design that encourages the vaunted Visitor
Pattern - without mock abuse!
> "As this code becomes more complex however, it becomes difficult to
> test cleanly because the generic println method used in printDetails
> loses information about our understanding of the domain. Instead, we
> can write a handler object to reify this dialogue between a stream and
> a Person:"
I think it's saying that the payload of println(x), the x part, will fall out of
the system, and that a mock can capture and test it.
--
Phlip
>> Thirdly, developing with Mock Objects tends to push behaviour towards
>> Visitor-like objects
>> [Gamma 1994] that are passed around; we call these Smart Handlers.
otherthread:
> Why have you posted something relating to a posting of mine, in a
> posting to a different person ... ??
As in you can now mock the handler object?
Not that I'm defending the design, but how would you design the
functionality?
>
> > The unit tests push us towards
> > writing domain code that refers only to local objects and parameters,
> > without an explicit policy
> > to do so.
>
> Obviously they aren't doing a very good job of pushing. This is an
> example of what happens when one *doesn't* do DFT. Look at what is
> happening here. The customized code for the mock objects and their
> infrastructure is probably going to be integer factors more that the
> code in the UUT itself! (It may be so repetitive compared to other UUT
> tests that the tester can write it while drunk, but there shouldn't be
> any need to write it in the first place.)
> Any time one has to resort to something this complex and customized to
> unit test a specific UUT there is something seriously wrong with the
> problem space abstraction of the UUT itself because it is bleeding
> cohesion all over the application. Fix that and you won't need mock
> objects, much less this sort of convoluted supporting infrastructure.
From what I gathered in the example, the UUT wouldn't be hard to test
in the very first instance, just pass in a mock person and a mock
writer. So I'm not quite sure the reasonings for redesigning in the
first place.... or why exactly the final solution is better than the
initial one, maybe someone can shed some light on this. IE: the reason
for redesign wasn't to make testing possible/easier (although somehow
considering testing the code did motivate the designer to redesign)
>
> <aside>
> FYI, the commercial translation IDEs usually provide a generic test
> harness OTB that can be used for unit testing. All the developer
> provides is specific test case data for the UUT in a text file. That's
> possible because the translation methodologies have to support full code
> generators for OOA models. Full code generators are only viable if the
> OOA model was constructed in a very disciplined OO fashion where methods
> are self-contained, cohesive, and properly encapsulated while
> collaborations separate message from method in an asynchronous
> communication model. Thus there are never any behavioral dependencies
> between methods so testing benefits because one doesn't need customized
> mock objects to emulate such dependencies.
> </aside>
Could you explain the aside a bit further? I didn't really understand
it (you have a few years on me in software design ;) )
Thanks
Taras
> Not that I'm defending the design, but how would you design the
> functionality?
So far the UUT, Person, seems rather uninteresting since the example
only identified 3 knowledge attributes because they are easy to
manipulate in an example. But testing those would not require mock
objects. I would need to see what behaviors Person has that would
require mock objects for testing before I can speculate on how it should
have been designed. However, the example below may be sufficient.
>> <aside>
>> FYI, the commercial translation IDEs usually provide a generic test
>> harness OTB that can be used for unit testing. All the developer
>> provides is specific test case data for the UUT in a text file. That's
>> possible because the translation methodologies have to support full code
>> generators for OOA models. Full code generators are only viable if the
>> OOA model was constructed in a very disciplined OO fashion where methods
>> are self-contained, cohesive, and properly encapsulated while
>> collaborations separate message from method in an asynchronous
>> communication model. Thus there are never any behavioral dependencies
>> between methods so testing benefits because one doesn't need customized
>> mock objects to emulate such dependencies.
>> </aside>
>
> Could you explain the aside a bit further? I didn't really understand
> it (you have a few years on me in software design ;) )
I think it is probably that I am used to test automation in translation
IDEs. Let me describe how that would work first and then draw some
parallels to how it would work in an elaboration environment.
In a translation IDE there is already infrastructure for full code
generation and model level execution. The IDE would make use of that to
automate construction of the tests. Consider Person so far:
Person {
private:
Name myName;
Age myAge;
Telephone myTelephone;
public:
Name getName();
Age getAge();
Telephone getTelephone();
void setName(Name);
void setAge(Age);
void setTelephone(Telephone);
Person(Name, Age, Telephone);
}
To unit test this object we need a test infrastructure for each
attribute, something like this <simplistic> example:
PersonTester::testName(Person myPerson)
{
for (i=0; i < myNameTestCount; i++)
{
myPerson.setName (myNameTestNames[i]);
name = myPerson.getName();
if (name != myNameTestNames[i])
myNameTestResults[i] = FALSE;
else
myNameTestResults[i] = TRUE;
}
}
where myNameTestCount and myNameTestNames contain test case data
initialized from an external file. IOW, we have test data consisting of
N candidate names for a Person with various lengths, embedded
punctuation, etc. and we put the results of each test case in
myNameTestResults for later analysis.
In this case PersonTester is the test harness. You could manually write
this for every to test, but that would be kind of tedious. The simplest
way the IDE could automate this is to just generate this code
automatically since has a code generator handy and it already has a
model that describes Person and its responsibilities. In effect, the IDE
would read a file that might look <very roughly> like:
Object:Person // tells IDE what the UUT is
Responsibility:Name // tells the IDE which responsibility to test
Test Count:3 // number of test cases for responsibiliyt
Irving // test case data
Clyde
Frumpkin
(Hint: note that this looks a lot like XML.)
The IDE will then create a PersonTester object with method for each
responsibility from the file and boilerplate to execute the individual
responsibility tests (invoking testName) and for interpreting the
results, which will look pretty much the same for all tests except for
names of attributes. It will write that test harness code for all the
responsibilities identified in the input file for all objects in the
input file. When that has been done, the user tells the harness to build
the test application and then execute it.
However, some IDEs are more sophisticated than this and they will use
the model simulator as a sort of interpreter. That interpreter will
essentially eat the same test case data file but it will interpret it in
the simulator with some special infrastructure for recording results and
whatnot. It will then "walk" each test case in debug mode; in some
simulators you can actually step through the test cases one line at a
time. (That is more complicated so I won't get into detail about how
that happens.)
So far there are no mock objects. Suppose we add a behavior:
Person::birthday()
{
myAge = myAge + 1;
}
To test this we need to initialize Person's age, invoke birthday(), and
check Person's age for a variety of possible initial ages. I won't take
up space describing it, but TestPerson::testBirthday will be created
with roughly the same structure and it will read the same sort of input
file for the test case data. The test case data will have to include the
expected results so that the appropriate attribute getters can be invoked.
So far, so good. This is all fairly straight forward (albeit tedious,
which is why we automate) and we have no mock objects. [TestPerson is a
pure test harness artifact to stimulate Person and record/analyse
results. In practice one can make this much more generic because
knowledge and behavior tests all look pretty much the same at this level
(and behaviors are self-contained, for the reasons below). The only
problem we would have is getting around the 3GL type system if we
generated the code. Even that would not be a problem in scripting-based
OOPLs where we can process ASCII strings to define types dynamically.]
But let's change the method slightly:
Person::birthday(x)
{
if (myAnotherObject.doIt(x))
myAge = myAge + 1;
}
Now we need a mock object because the specification of myAge after 'x'
has been processed depends upon AnotherObject.doIt() doing the right
thing. IOW, for any given value of 'x' we must stub AnotherObject.doIt()
and provide the right boolean return for the value of 'x' in the test
case. Thus our test harness must emulate the behavior of
AnotherObject.doIt(). (If we don't stub, then a correct implementation
must be linked in and we are doing an in situ functional test rather
than a unit test.)
The problem here is that Person.birthday() is not properly
self-contained in an OO sense and it has a hierarchical implementation
dependence on AnotherObject.doIt(). That dependence is what requires the
use of mock objects. We would eliminate that dependence in the design in
this trivial case by using the original definition of Person.birthday()
while connecting the flow of control dots differently. The client who is
currently invoking Person.birthday() would invoke AnotherObject.doIt()
instead. Then AnotherObject.doIt() would invoke Person.birthday --
provided the result of its operation was TRUE. [There is a rigorous DbC
technique for getting the flow of control right, but that's a different
story.] Now both responsibilities can be unit tested in complete
isolation; we just need to validate that AnotehrObject.doIt() invokes a
no-op stub for Person.birthday when and only when its operation is TRUE.
For full test automation one also needs to specify what messages the
behavior responsibility is expected to generate in the test case data
file. The tricky part of that is ensuring it gets to the right object,
but the OOA model already has the relationships defined that would be
navigated so that can be validated as well.
[The translation IDEs use a neat trick for logging messages from
behaviors. Since all translation methodologies insist in using object
state machines, any outgoing messages will be events. So one just
provides a switch on the event queue manager so that it only pops and
processes the first event, which is the stimulus event for the state
action. Then it just logs any event placed on the queue by the UUT for
analysis by the test harness. The event includes the address of the
receiving object in memory, so that can be compared to the addresses
when objects were instantiated.]
So how would one do this if one did not have a model simulator or full
code generator handy? You could use a scripting language like perl to
construct the test harness code based on the same sort of input file
with somewhat more complicated lexical replacement. It would be fairly
complex but it would only have to be done once. If one extracts the
invariants of unit testing, much of the testing looks very similar. As I
indicated above, in practice one can write quite generic code for the
test harness, especially in one of the scripting-based OOPLs.
I don't want to understate the complexity of building such an automated
test harness. There are a lot of nasty little issues to be resolved
(e.g., access to private attributes) and one needs to be clever about
how the invariants of unit testing are abstracted. But it is a one-time
effort because the same harness can be applied to any application once
one gets around the type system issues for mapping ASCII identity to run
time instances. One just has to provide a different test case data file.
>> I think it's saying that the payload of println(x), the x part, will fall out of
>> the system, and that a mock can capture and test it.
> As in you can now mock the handler object?
There are two kinds of mocks in the world - call them "dynamic" and "static".
A dynamic mock, such as Mocha for Ruby, looks like this:
object.expects(:method).returns(42)
result = object.foo() # calls method and adds 1
assert{ result == 43 }
The Mocha library wrapped a real object and replaced only one of its methods.
A static mock, in a Java-like language, might look like this:
class MockMyClass: inherits MyClass
{
int method() { return 42; }
};
MockMyClass object = new(MockMyClass);
int result = object.foo(); // calls method and adds 1
assert_equal 43, result
Never mind the static version is several times more verbose and redundant than
the dynamic one. I think modern Java also supports dynamic mocks.
Now suppose that our target line (our Activate line in the
Assemble-Activate-Assert Pattern) cannot take a mocked MyClass directly. Mocha
dynamically solves that problem like this:
MyClass.any_instance.expects(:method).returns(42)
activate(); # calls object.foo() for us
That means that if anyone uses any MyClass, its .method() will do nothing but
return 42, and the test case will fail if nobody calls .method().
To use a static mock like that, I must insert the MockMyClass into the code at
the right juncture for the Activate line to then use it. This implies that an
activate() method, in Java, cannot simply call new(MyObject) inside itself.
This topic is "Construction Encapsulation". Nobody constructs anything they
need; they always receive everything from callers.
I suspect the authors you cited discovered that, in their situation,
Construction Encapsulation lead to the Visitor Pattern.
--
Phlip