PDA

View Full Version : Mocking local objects


Alvin777
07-01-2003, 01:36 AM
I have following C++ code:

SomeClass::ShowDialog()
{
SomeDlgClass oDlg;
oDlg.DoModal();
m_Value = oDlg.GetSomeUserEnteredValue();
}

I'd like to test SomeClass and use mock object instead of
SomeDlgClass. That mock object do not display dlg at DoModal() and
returns expected value at GetSomeUserEnteredValue().

But how can I replace SomeDlgClass with MockedSomeDlgClass without
changing the code?

It is easy if I'd used pointers:

SomeClass::ShowDialog(SomeDlgClass* pDlg)
{
pDlg->DoModal();
m_Value = pDlg->GetSomeUserEnteredValue();
}

Then I just should call ShowDialog() with mock object. But what to do
if object created locally?

--
Alvin777

4Space
07-01-2003, 01:59 AM
Hi Alvin,

You've got the classic problem of hard wired dependencies getting in the way
of your testing. Of course, you're on the right track, Mock objects are the
way to go. But you must also break internal dependencies as well as external
ones. Rather than try and relay the techniques for this to you, perhaps I
can redirect you to a paper on this area:

http://www.informatik.fernuni-hagen.de/import/pi3/stefan/Publications/CONQUEST02.pdf

I'm sure I've read articles on it at objectmentor also.

Seperation of concerns is your friend :)

Cheers,

4Space

"Alvin777" <alvin777@mail.ru> wrote in message
news:1c9319e1.0307010136.25ffe739@posting.google.com... I have following C++ code: SomeClass::ShowDialog() { SomeDlgClass oDlg; oDlg.DoModal(); m_Value = oDlg.GetSomeUserEnteredValue(); } I'd like to test SomeClass and use mock object instead of SomeDlgClass. That mock object do not display dlg at DoModal() and returns expected value at GetSomeUserEnteredValue(). But how can I replace SomeDlgClass with MockedSomeDlgClass without changing the code? It is easy if I'd used pointers: SomeClass::ShowDialog(SomeDlgClass* pDlg) { pDlg->DoModal(); m_Value = pDlg->GetSomeUserEnteredValue(); } Then I just should call ShowDialog() with mock object. But what to do if object created locally? -- Alvin777

Michael Feathers
07-01-2003, 03:07 AM
"Alvin777" <alvin777@mail.ru> wrote I have following C++ code: SomeClass::ShowDialog() { SomeDlgClass oDlg; oDlg.DoModal(); m_Value = oDlg.GetSomeUserEnteredValue(); } I'd like to test SomeClass and use mock object instead of SomeDlgClass. That mock object do not display dlg at DoModal() and returns expected value at GetSomeUserEnteredValue(). But how can I replace SomeDlgClass with MockedSomeDlgClass without changing the code? It is easy if I'd used pointers: SomeClass::ShowDialog(SomeDlgClass* pDlg) { pDlg->DoModal(); m_Value = pDlg->GetSomeUserEnteredValue(); }

Here is a refactoring I called 'parameterize method':

You have this code:

void SomeClass::ShowDialog()
{
SomeDlgClass oDlg;
oDlg.DoModal();
m_Value = oDlg.GetSomeUserEnteredValue();
}

Create a new empty method with the same name:

void SomeClass::ShowDialog()
{
}

Copy in the the code from the original method except for the declaration of
the local:

void SomeClass::ShowDialog()
{
oDlg.DoModal();
m_Value = oDlg.GetSomeUserEnteredValue();
}

Your compiler yells at you because it needs to know what oDlg is, so add
oDlg but as a parameter:

void SomeClass::ShowDialog(SomeDlgClass& oDlg)
{
oDlg.DoModal();
m_Value = oDlg.GetSomeUserEnteredValue();
}

Replace the code in the original ShowDialog with a call to the new one:

void SomeClass::ShowDialog()
{
SomeDlgClass oDlg;
ShowDialog(oDlg);
}

Your code should now look like this: two ShowDialog methods. If you want to
test, you can call the latter one with a mock.

void SomeClass::ShowDialog()
{
SomeDlgClass oDlg;
ShowDialog(oDlg);
}

void SomeClass::ShowDialog(SomeDlgClass& oDlg)
{
oDlg.DoModal();
m_Value = oDlg.GetSomeUserEnteredValue();
}


Michael Feathers
www.objectmentor.com

Michael Feathers
07-02-2003, 07:44 AM
"Alvin777" <alvin777@mail.ru> wrote in message
news:1c9319e1.0307020650.352fb9fc@posting.google.com... Thanx! Looks like the thing I need.

You're welcome. One thing I forgot to mention. Often when I do
'parameterize method', I put a "with" in the name I am extracting. In your
example you'd end up with:

void SomeClass::ShowDialog()

and:

void SomeClass:ShowDialogWithDialog(SomeDlgClass& oDlg)

There are times when it looks less goofy than this.

Michael Feathers
www.objectmentor.com

Michael Feathers
07-02-2003, 09:34 AM
"Greg Bacon" <gbacon@hiwaay.net> wrote in message
news:vg62e44033ho06@corp.supernews.com... In article <bdrq3n$s4i$1@slb4.atl.mindspring.net>, Michael Feathers <mfeathers@objectmentorNOSPAM.com> wrote: : [...] : : Your code should now look like this: two ShowDialog methods. If you : want to test, you can call the latter one with a mock. : : void SomeClass::ShowDialog() : { : SomeDlgClass oDlg; : ShowDialog(oDlg); : } : : void SomeClass::ShowDialog(SomeDlgClass& oDlg) : { : oDlg.DoModal(); : m_Value = oDlg.GetSomeUserEnteredValue(); : } How do you write a test to mitigate the risk of skew between the canned and parameterized versions of ShowDialog?

In general, I just don't do it. Refactorings like this make testing
possible where it wouldn't be otherwise. When I do these I think of what
could go wrong and do the refactoring carefully with a partner. In some of
these 'refactorings-to-get-things-under-test,' the worst thing that can
happen is something I call a 'connection error': you forget to make a call
in the code when you should have made a call. In this case, if we botched
the parameterize method refactoring, the dialog box would just not show up
in the application. It is something you can make note of and check for.

It's good to be conscious of the slippery slope. You shouldn't have to do
too many of these unprotected refactorings to get things under test.
Would it be better to divert everyone to the parameterized method?

I don't. The original method was convienient for the clients, so I let then
use that. Moreover, if they do use it, we can see whether we made a
connection error quickly.

Sometimes the parameterized method is found useful by a client. That's
great, frosting on the cake.

Michael Feathers
www.objectmentor.com


MyLounge.com Site Map
Forum: Cars, Cell Phone, Database, Games, Home Improvement, IT, Music, School, Sports, Web Design, Web Server, Weight Loss

The MyLounge.com forum is intended for informational use only and should not be relied upon and is not a substitute for any advice. The information contained on MyLounge.com are opinions and suggestions of members and is not a representation of the opinions of MyLounge.com. MyLounge.com does not warrant or vouch for the accuracy, completeness or usefulness of any postings or the qualifications of any person responding. Please consult a expert or seek the services of an attorney in your area for more accuracy on your specific situation. Please note that our forums also serve as mirrors to Usenet newsgroups. Many posts you see on our forums are made by newsgroup users who may not be members of MyLounge.com Term of Service