Skip to main content

Unit testing and PInvoking pt.3

In the previous parts of this short series (that took a long time to finish) I looked at testing code that depends on pinvoke calls. I did this by wrapping the PInvoked functions in a class and then injecting that class into the depending code. I used the wrapper class to put some checks around the platform invokes to smooth some rough edges of the calls to unmanaged land. This creates a nice reusable interface around these calls but now we have the same problem we started with, testability. In this post I'll look at adding a final layer of indirection to be able to test this code.

You can find the previous posts in this series here.

First I'll start with a warning. Because C# forces us to import Dll functions as static functions it's hard to mock them without writing extra code that doesn't really add any functionality. But in most cases I think the added testability is worth the extra verbosity. But it's not the nicest code ever written.

Now lets look back at where we were.

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

The SetupApi is mockable with rhino mocks so that we can test consuming code. Now let's see how we can test the SetupDiClassNameFromGuid function.


If you're using TypeMock you can stop reading now. TypeMock is a very powerfull mocking framework that can easilly mock the static DllImport. I'm using RhinoMocks. This means I'll have to do some extra work.


First the ugly stuff I warned you about at the start of the article. We'll have to put an adapter around the DllImported function to make it non-static.



   1: private bool SetupDiClassNameFromGuid(
   2:     ref Guid classGuid, StringBuilder className, UInt32 classNameSize, out UInt32 requiredSize)
   3: {
   4:     return _SetupDiClassNameFromGuid(ref classGuid, className, classNameSize, out requiredSize);
   5: }

Now we could just make this function public and use a partial mock to insert our test-code but this would open up the raw DllImport to the outside world. This is not something I'm a big fan of. And we have three different functions called something like SetupDiClassNameFromGuid. So things are getting a bit confusing.


This is the way I usually clean up this mess. With an inner class for the DllImport and some constructor magic to make things injectable.



   1: public class SetupApi
   2: {
   3:     public class DllImport
   4:     {
   5:         [DllImport("setupapi.dll",
   6:             EntryPoint = "SetupDiClassNameFromGuid",
   7:             CharSet = CharSet.Auto,
   8:             SetLastError = true)]
   9:         private static extern bool _SetupDiClassNameFromGuid(
  10:             ref Guid classGuid, StringBuilder className, UInt32 classNameSize, out UInt32 requiredSize);
  11:  
  12:         public virtual bool SetupDiClassNameFromGuid(
  13:             ref Guid classGuid, StringBuilder className, UInt32 classNameSize, out UInt32 requiredSize)
  14:         {
  15:             return _SetupDiClassNameFromGuid(ref classGuid, className, classNameSize, out requiredSize);
  16:         }
  17:     }
  18:  
  19:     private DllImport _import;
  20:  
  21:     public SetupApi() : this(new DllImport()) { }
  22:  
  23:     public SetupApi(DllImport import)
  24:     {
  25:         _import = import;
  26:     }
  27:  
  28:     public virtual bool SetupDiClassNameFromGuid(Guid classGuid, out string className)
  29:     {
  30:         // bla...
  31:  
  32:         returnValue = _import.SetupDiClassNameFromGuid(
  33:             ref classGuid, classNameBuilder, (uint)classNameBuilder.Capacity, out reqSize);
  34:  
  35:         // bla...
  36:     }
  37: }

As you can see I added a parameterless constuctor for use in normal code. Normally I don't do this but in this case we're pretty sure the only two uses of this class will be with the inner class in day-to-day life and with a mocked inner class for tests. So this will clean up calling code a bit.


Here is what a test might look like, here we're testing if our wrapper calls the pinvoked call with an initial buffer of 50.



   1: [Test]
   2: public void ShouldTryCallWithBufferLength50()
   3: {
   4:     var mockDll = MockRepository.GenerateMock<SetupApi.DllImport>();
   5:     var api = new SetupApi(mockDll);
   6:  
   7:     string className;
   8:     var classGuid = Guid.NewGuid();
   9:  
  10:     mockDll.Expect(
  11:         x => x.SetupDiClassNameFromGuid(
  12:             ref Arg<Guid>.Ref(Is.Anything(), classGuid).Dummy,
  13:             Arg<StringBuilder>.Is.Anything, 
  14:             Arg<uint>.Is.Equal((uint)50), 
  15:             out Arg<uint>.Out(51).Dummy))
  16:         .Return(true);
  17:  
  18:     api.SetupDiClassNameFromGuid(classGuid, out className);
  19:  
  20:     mockDll.VerifyAllExpectations();
  21: }

And another one to see if there's a retry when the buffer is bigger than 50



   1: [Test]
   2: public void ShouldRetryIfStringTooLarge()
   3: {
   4:     var mockDll = MockRepository.GenerateMock<SetupApi.DllImport>();
   5:     var api = new SetupApi(mockDll);
   6:  
   7:     string className;
   8:     var classGuid = Guid.NewGuid();
   9:  
  10:     mockDll.Stub(
  11:         x => x.SetupDiClassNameFromGuid(
  12:              ref Arg<Guid>.Ref(Is.Anything(), classGuid).Dummy,
  13:              Arg<StringBuilder>.Is.Anything,
  14:              Arg<uint>.Is.Equal((uint)50),
  15:              out Arg<uint>.Out(51).Dummy))
  16:         .Return(true);
  17:  
  18:     api.SetupDiClassNameFromGuid(classGuid, out className);
  19:  
  20:     mockDll.AssertWasCalled(
  21:         x => x.SetupDiClassNameFromGuid(
  22:             ref Arg<Guid>.Ref(Is.Anything(), classGuid).Dummy,
  23:             Arg<StringBuilder>.Is.Anything,
  24:             Arg<uint>.Is.Equal((uint)50),
  25:             out Arg<uint>.Out(51).Dummy));
  26: }

By the way. I really like the rhino mocks AAA model. Using it together with the Arg object like here makes it still rather readable to mock and assert calls to rather complex functions with ref and out parameters mixed together. Although I did find one small problem. If you remove the cast to uint on line 24 the code still compiles but the test fails. Int 50 should be equal to uint 50 but this doesn't work here. But this could be a problem with the rhino mocks 3.5 beta I'm using.

Comments

Popular posts from this blog

Using xUnit.Net with .Net 4.0

I’ve been using xUnit.Net for a while now. It’s just a tiny bit cleaner and slightly less abrasive than other .Net unit testing frameworks. Leaving out unnecessary stuff like [TestFixture] and shortening Assert.AreEqual to the equally clear but shorter Assert.Equal don’t seem like big improvements but when you type them several times a day tiny improvements start to add up. I also like the use of the [Fact] attribute instead of [Test]. It shifts the focus from testing to defining behavior. So how do we get all this goodness working with the Visual Studio 2010 beta?

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 and …

Building Android projects with Jenkins, Ant and Mercurial

I have recently set up a Jenkins build server for my Android projects hosted on Bitbucket. It’s not difficult but there are a couple pitfalls and the information on how to do this isn’t available from one single place so I decided to document the process and put up the information over here. Maybe other people will benefit from having a step-by-step guide too.