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.
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.
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.
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.
And another one to see if there's a retry when the buffer is bigger than 50
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.