Mocking Win32 API

Mocking Win32 API

Post by John Elric » Tue, 08 Jul 2003 21:47:31




SNIP

Quote:> I've
> > learned that a problem like this is a smell that there is a violation of
> > DIP.

> Yep, the real issue is: why are we mocking at all?  If I'm trying to get a
> C++ class into a test harness to add a feature or do some refactoring
> unrelated to the offending functions, I'll often do the dummy function
trick
> just to get past all of that direct API usage, to deal with one problem at
a
> time.

> > That's why I'd move to an adapter based on an interface, with an
interface
> > based factory to generate it.  Makes it _very_ easy to swap out test vs
> > production or different production implementations.

> Adapters are great, but I don't like to "settle" the interface too soon.
> Even though a signature like: "HRESULT MAPILogonEx(ULONG ulUIParam, LPTSTR
> lpszProfileName, LPTSTR lpszPassword, FLAGS flFlags, LPMAPISESSION FAR *
> lppSession)" is a crime against nature, often I'll mock it directly by
> adding a method with exactly that signature to a class.  Then when I can
go
> back and forth with the mock and production classes I'll start to make
> higher level methods hat eventually hide the API, often I get them by
paying
> attention to duplication and feature envy.  The nice thing is, with the
mock
> function in place, all the code that I move onto the new class can be
> tested.  And, that is pretty good because it can safely grow into a
healthy
> abstraction, something more than just an adapter.

Hmmm...so you're saying that by mocking the function, you are delaying the
need to update two interfaces (the production and the test) until the
interface signature defines itself via its needs...if I'm reading right.

That makes more sense to me now.

John

 
 
 

Mocking Win32 API

Post by Michael Feather » Tue, 08 Jul 2003 23:36:46





> SNIP

> > I've
> > > learned that a problem like this is a smell that there is a violation
of
> > > DIP.

> > Yep, the real issue is: why are we mocking at all?  If I'm trying to get
a
> > C++ class into a test harness to add a feature or do some refactoring
> > unrelated to the offending functions, I'll often do the dummy function
> trick
> > just to get past all of that direct API usage, to deal with one problem
at
> a
> > time.

> > > That's why I'd move to an adapter based on an interface, with an
> interface
> > > based factory to generate it.  Makes it _very_ easy to swap out test
vs
> > > production or different production implementations.

> > Adapters are great, but I don't like to "settle" the interface too soon.
> > Even though a signature like: "HRESULT MAPILogonEx(ULONG ulUIParam,
LPTSTR
> > lpszProfileName, LPTSTR lpszPassword, FLAGS flFlags, LPMAPISESSION FAR *
> > lppSession)" is a crime against nature, often I'll mock it directly by
> > adding a method with exactly that signature to a class.  Then when I can
> go
> > back and forth with the mock and production classes I'll start to make
> > higher level methods hat eventually hide the API, often I get them by
> paying
> > attention to duplication and feature envy.  The nice thing is, with the
> mock
> > function in place, all the code that I move onto the new class can be
> > tested.  And, that is pretty good because it can safely grow into a
> healthy
> > abstraction, something more than just an adapter.

> Hmmm...so you're saying that by mocking the function, you are delaying the
> need to update two interfaces (the production and the test) until the
> interface signature defines itself via its needs...if I'm reading right.

> That makes more sense to me now.

Yes.  One of the sad things when you work with legacy code is that you want
to do something and you want to do it 'right' but you if you are doing it
'right' you can end up with a lot of work between you and the narrow thing
that you need to get done (add a feature for instance).  I like to get to
the narrow thing with safe but not pretty steps and then go back and clean
things up as needed.

Michael Feathers
www.objectmentor.com

 
 
 

Mocking Win32 API

Post by Dale Kin » Thu, 10 Jul 2003 08:32:17







> > SNIP

> > > I've
> > > > learned that a problem like this is a smell that there is a
violation
> of
> > > > DIP.

> > > Yep, the real issue is: why are we mocking at all?  If I'm trying to
get
> a
> > > C++ class into a test harness to add a feature or do some refactoring
> > > unrelated to the offending functions, I'll often do the dummy function
> > trick
> > > just to get past all of that direct API usage, to deal with one
problem
> at
> > a
> > > time.

> > > > That's why I'd move to an adapter based on an interface, with an
> > interface
> > > > based factory to generate it.  Makes it _very_ easy to swap out test
> vs
> > > > production or different production implementations.

> > > Adapters are great, but I don't like to "settle" the interface too
soon.
> > > Even though a signature like: "HRESULT MAPILogonEx(ULONG ulUIParam,
> LPTSTR
> > > lpszProfileName, LPTSTR lpszPassword, FLAGS flFlags, LPMAPISESSION FAR
*
> > > lppSession)" is a crime against nature, often I'll mock it directly by
> > > adding a method with exactly that signature to a class.  Then when I
can
> > go
> > > back and forth with the mock and production classes I'll start to make
> > > higher level methods hat eventually hide the API, often I get them by
> > paying
> > > attention to duplication and feature envy.  The nice thing is, with
the
> > mock
> > > function in place, all the code that I move onto the new class can be
> > > tested.  And, that is pretty good because it can safely grow into a
> > healthy
> > > abstraction, something more than just an adapter.

> > Hmmm...so you're saying that by mocking the function, you are delaying
the
> > need to update two interfaces (the production and the test) until the
> > interface signature defines itself via its needs...if I'm reading right.

> > That makes more sense to me now.

> Yes.  One of the sad things when you work with legacy code is that you
want
> to do something and you want to do it 'right' but you if you are doing it
> 'right' you can end up with a lot of work between you and the narrow thing
> that you need to get done (add a feature for instance).  I like to get to
> the narrow thing with safe but not pretty steps and then go back and clean
> things up as needed.

I would add that you still haven't eliminated the need to mock the original
function by using an adapter. The test code for the adapter still needs to
mock it to be tested, so the problem remains although you do at least
isolate it to only one spot.
--
 Dale King
 
 
 

Mocking Win32 API

Post by John Elric » Thu, 10 Jul 2003 09:13:36



SNIP

Quote:> I would add that you still haven't eliminated the need to mock the
original
> function by using an adapter. The test code for the adapter still needs to
> mock it to be tested, so the problem remains although you do at least
> isolate it to only one spot.

Indeed...and isolating the problems to a very narrow area can be worth quite
a lot.

John

 
 
 

Mocking Win32 API

Post by Dale Kin » Thu, 10 Jul 2003 23:59:42





> > How can I use something like mock objects when calling Win API
> > functions like this?

> > [C++]

> > class WrMAPISession
> > {
> > public:
> > void Logon();
> > ...
> > }

> > void WrMAPISession::Logon()
> > {
> > ...
> > MAPILogonEx(....);
> > ...
> > }

> > Write a class, which consists of one function - MAPILogonEx - and use
> > its instance as a parameter when calling Logon()? That doesn't sound
> > good.

> It's just a function, right?  Here are two techniques:

I have used both techniques.

- Show quoted text -

Quote:> 1) If you have your class in files named wrmapisession.h, and
> wrmapisession.cpp make another file called testwrmapisession.cpp.  Is
should
> be empty except for this line:

>    #include "wrmapisession.cpp"

> Compile testwrmapisession.cpp and link it to your testharness.

> When you want to mock out a function, add it to the file before the
include:

>    /* include whatever makes windows happy here */
>    HRESULT MAPILogonEx(
>      ULONG ulUIParam,
>      LPTSTR lpszProfileName,
>      LPTSTR lpszPassword,
>      FLAGS flFlags,
>      LPMAPISESSION FAR * lppSession
>    ) { return S_OK; }

> #include "wrmapisession.cpp"

Another variation on this technique that I have used so that I do not
collide at link time is to use the preprocessor to change the name of the
function. So for instance if I want to use fooMapiLogonEx as the function
name I would:

#define MAPILogonEx fooMapiLogonEx

 #include "wrmapisession.cpp"

   /* include whatever makes windows happy here */
   HRESULT fooMapiLogonEx(
     ULONG ulUIParam,
     LPTSTR lpszProfileName,
     LPTSTR lpszPassword,
     FLAGS flFlags,
     LPMAPISESSION FAR * lppSession
    ) { return S_OK; }

This modifies the code to substitute my function name. This way other code
will link with the real function, but the code I am testing will use this
alternate version. This lets you mock it differently for multiple files
under test.
--
 Dale King