Skip to main content

Unit testing and PInvoking

I'm big on unit testing, I like to test everything and I feel uncomfortable when I'm unable to write tests for some of my code. So even though some parts of your application are hard to test I'm willing to take some extra time to make them testable. In this post I want to show you some tricks I use to unit-test code that uses platform invoked functions.

As an example I'll use one of the invokes from my previous post, SetupDiClassNameFromGuid from the setupapi.dll. If you want to know more about what this does can read that post. Now let's take a look at some code.

   1: public class PInvoking
   2: {
   3:     [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
   4:     public static extern bool SetupDiClassNameFromGuid( 
   5:         ref Guid classGuid, StringBuilder className, UInt32 classNameSize, ref UInt32 requiredSize);
   7:     public void DoSomethingThatNeedsToBeTested() 
   8:     {
   9:         var className = new StringBuilder();
  10:         var classGuid = Guid.Empty;
  11:         var reqSize = (uint)0;
  13:         // Some code that needs to be tested
  14:         // ..
  16:         // PInvode preprocessing
  17:         // ..
  19:         SetupDiClassNameFromGuid(ref classGuid, className, reqSize, ref reqSize);
  21:         // PInvoke postprocessing
  22:         // ..
  24:         // Some more code
  25:         // ..
  26:     }
  27: }

I can see two problems here, testability and reusability. Let's start with making this more reusable although this has nothing to do with unit testing yet.

If you read the previous post you'll know that this call needs some pre-, and post-processing to get something usefull out of it. These technical details are not something you want to bother the consuming function with. DoSomethingThatNeedsToBeTested() just wants to pass a Guid in and get a string back. And in the real world there are probably more consumers of this imported function so you'll want to get rid of this duplicated code and hide it somewhere you can reuse it with a single call.

You can split out the preprocessing and postprocessing into a separate function but it would be better to go a bit further and just put everything in a separate class. I even made the DllImport private so the consumers are forced to use the code with all the wrapping code. I used the wrapper code from my previous post here to show how fast things get complex. In real life you'll probably want to add some errorhandling too.

   1: public class SetupApi
   2: {
   3:     [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
   4:     private static extern bool _SetupDiClassNameFromGuid(
   5:         ref Guid classGuid, StringBuilder className, UInt32 classNameSize, ref UInt32 requiredSize);
   7:     public static bool SetupDiClassNameFromGuid(Guid classGuid, ref string className)
   8:     {
   9:         // 50 is a Sensible default value, if we're lucky it's enough
  10:         uint reqSize = 50;
  11:         bool returnValue;
  12:         StringBuilder classNameBuilder = new StringBuilder((int)reqSize);
  14:         // First try.
  15:         returnValue = _SetupDiClassNameFromGuid(
  16:             ref classGuid, classNameBuilder, (uint)classNameBuilder.Capacity, ref reqSize);
  18:         if ((uint)classNameBuilder.Capacity != reqSize)
  19:         {
  20:             // call again with the right size stringbuilder
  21:             classNameBuilder.Capacity = (int)reqSize;
  22:             returnValue = _SetupDiClassNameFromGuid(
  23:                 ref classGuid, classNameBuilder, reqSize, ref reqSize);
  24:         }
  26:         className = classNameBuilder.ToString();
  27:         return returnValue;
  28:     }
  29: }

Things are a bit more reusable now. But they're still not testable. And we've created a second problem. We don't just want to test the consuming code but we want to test the wrapper around _SetupDiClassNameFromGuid too.

The first problem is easy to solve. Just remove the static keyword from line 7. Then you can use dependency injection to inject this class into the consuming classes or functions and you can mock up everything. I'll show an example in my next post.

The second problem is a bit harder. I have a couple of solutions for this too but the are not very straight-forward and involve some extra code. I'll explain these later too.


Popular posts from this blog

Square One available on the Android market

This is just a short post to let you know that a first version of the Android app I’ve been working on for the last couple of weeks is available on the Android market . The app is called Square One and it’s a simple bassline synthesizer. It’s free so try it out and let me know what you think of it, but be prepared it’s still an early version. I hope to add more features in the next few months and maybe build something that can be used to create real music. The lower part of the screen contains the sequencer controls that can be used to program your own bass lines. On the left is a four by four grid of buttons where you can select a step in the sequence. On the right you can select the note to be played on that step. When you’re done you can press Start and the sequence starts playing. The knobs on the top can be used to control a couple of parameters from the synthesizer engine that creates the sound. You can control the cutoff frequency and resonance of the low-pass filter, attack an

Android development resource links

I've been playing with the Android SDK and I have a growing list of bookmarks to Android dev resources for my own use. I thought the best place to keep them would be here on my blog. That way other people can benefit too. I'll keep updating this list so feel free to add suggestions in the comments.

Acer Iconia A500 review

A couple of weeks ago I bought an Android tablet, the Acer Iconia A500. Today I received an email from Acer asking to fill in a short survey to tell them what I like and don't like about my new tablet. I might as well share my thoughts on this tablet in a short review on my blog.