Mock32, parameterize from below

| 7 Comments
It's funny how potential product ideas beget other potential product ideas and the thing that you eventually end up with as a product for sale is often far from the original product idea and the code that you started to write... I'm not even there yet and I'm on my third wave of product idea focus. Right now I'm working towards getting some developer tools out there to help people write better systems. This is the API hooking stuff that I've blathered on about in the past. The original product idea that started it all was a niche market testing tool that was too big an idea for me to focus on it to start with. Part of that tool required mocking out of Win32 API calls to allow for controlled testing of the target and so the wildly ambitious product 1 gave birth to the slightly more focused product 2.

Product 2 became a bit bogged down in the detail. It's a much more generic tool than either product 1 or product 3 and as such it presented several problems that I wasn't ready to face and so I moved on to product 3. Product 3 is the suite of API hooking based debugger tools. These are much easier to understand from a 'who's my market' point of view than product 2, though not as niche as product 1. The handy thing is that they're built on much of the support code that was required for product 2. Anyway, enough of the family tree, I've decided to talk about product 2 for a change.

Product 2 was something that I, rather grandly, named Mock32. The idea being that you could use it to do things like this:

   CMockEvent mock;

   mock.Expect(_T("CreateEvent"))
      .Taking(_T("lpEventAttributes"), CNullPointer())
      .Taking(_T("bManualReset"), CFunctionCallData(FALSE))
      .Taking(_T("bInitialState"), CFunctionCallData(FALSE))
      .Taking(_T("lpName"), CNullPointer());                   // Should be able to specify returning 'index 1'
                                                               // or extract the return value?
   mock.Expect(_T("CloseHandle"))
      .Taking(_T("hObject"), CFunctionCallData())              // Should be able to pass 'index 1'
      .Returning(_T("hObject"), CFunctionCallData())
      .Returning(CFunctionCallData(TRUE))
      .LastError(ERROR_SUCCESS);

   {
      CAutoResetEvent event;
   }

   THROW_ON_FAILURE(functionName, true == mock.Verify());

And test the lowest level of your code by using mocking and dependency injection techniques to replace and control parts of the Win32 API.

It works. The code above and much more are now part of my Win32Tools test harness suite. My problem was one of moving the workingish code to a product and dealing with the questions of licensing and whatever. It was clear, at the time, that although I could use this stuff it wasn't necessarily easy for anyone else to use; what's more, I'm not even sure that anyone else would want to test at this level...

The more useful tests being ones where you have the mock fail a call which is often complex to force a failure in unexceptional conditions...

   CMockEvent mock;
  
   mock.Expect(_T("CreateEvent"));
  
   mock.Expect(_T("SetEvent"));
  
   mock.ExpectAndReturn(_T("SetEvent"))
      .Returning(CFunctionCallData(FALSE))
      .LastError(ERROR_NOT_ENOUGH_MEMORY);
  
   mock.Expect(_T("CloseHandle"));
  
   {
      CEvent event(0, CEvent::AutoReset, CEvent::NonSignaled);
  
      event.Set();
  
      THROW_ON_NO_EXCEPTION(functionName, event.Set);
   }
  
   THROW_ON_FAILURE(functionName, true == mock.Verify());

The way the mock is set up above it causes the second call to ::SetEvent() that is made to fail and return FALSE with a lastError of ERROR_NOT_ENOUGH_MEMORY. This allows us to force execution of the error handling code path in the class under development; something that would otherwise be extremely difficult to do.

This is the code that I originally developed my code generator for. The code generator can generate much of the code that is required to mock an API by simply running it on the appropriate section of a windows header file.

This is also the code that Roy Osherove's comments convinced me to change from a purely log based mock system to one which used expectations. You'll note the JMockesque style of the mock setup. This was well worth doing and moved the code slightly nearer to being something that someone other than me would find useful.

I paused development due to the sheer scale of the work involved. Sure I needn't focus on the whole of the Win32 API to start with but at present the code generator needs adjustments almost every time I decide to add new set of mock functions and the expectations need a lot of work before they'll be as useful as I want them to be. It's unfortunate that the mocks can't "fail fast" in the same way that JMock can; if we detect an expectation failure in an API call there's nothing we can do about it until the call returns. Throwing an exception is a no-no as the Win32 API doesn't do that. Dumping the user in the debugger with an int 3 is less than useful in a test. Hence the need for an explicit verification call; this design was still a work in progress when I pressed the pause button on the project. Still, the work was a useful step towards my current product ideas and there's nothing to stop me going back to it in the future as and when I get the time and inclination.

7 Comments

Hi Len,

Cool stuff!

Why is the class named CMockEvent? It seems to me the same code should be useful for mocking just about any Win32 function. Or is it hard-wired to recognize the event functions?

Cheers,
- Kim

Right now the code generator generates mocks based on the windows header file and groups them into classes according to how I cut chunks out of the header file. The generator also generates API hooks for each of the functions which are function specific and then wires the lot together. So you'd have a mockXXX for each thing you want to mock out. MockCriticalSection, etc. Yes I could probably just put them all in the same Mock32 class and that may be easier to manage...

Actually, another good reason for making the mocks reasonably small in surface area is that it means you can focus your testing on a particular area of the Win32 API. You often wont want to have to add all of the expectations for all of the calls that you may be making...

Hey Buddy

That's a cool stuff and an another good reason for making the mocks.

Hi len

How are you doing? Great work buddy I really enjoyed it:)

Keep up the good work!!

Can't you use Win32 structured exception handling (SEH) to fail early?

I could but it didn't really seem appropriate. I'll revisit the idea when I next have a play with the code...

Leave a comment