Friday, July 02, 2010

Testing Motorola EMDK WLAN implementation on the desktop

I've been writing about testing recently and whether or not to use device test projects or desktop test projects. You can read my view on this in a previous blog post.

No doubt if you have written code for any of the Motorola rugged devices (or other rugged OEM devices) you might have encountered the EMDK WLAN class (or similar if not coding against the EMDK) - which wraps the low-level mobile specific Motorola Fusion API. This is a prime example of why you should use a desktop test project rather than device project. Hang on, you just said the Fusion API can only execute on the device, so don't we need a device test project? No. The reason is simple, again if you have a continuous integration, automated build process setup, your tests will be executing on the build server so you won't be able to execute those tests against a real Motorola device. If you have a device test project, the best you can do is execute those tests on the Windows Mobile emulator - but what will this prove? In this case this is really no different in terms of a test problem than executing them on the desktop.

So this is a reason to write your WLAN tests within a desktop test project. Let me demonstrate...

The WLAN class that comes with the EMDK (Symbol.Fusion.WLAN.WLAN) is a prime example, to make things worst, it doesn't implement an interface, take a look:



So this makes it almost impossible to test. Instead how I have overcome this problem is to write an adapter that wraps the WLAN class, then mock out the adapter using Rhino Mocks. Observe the following WLAN implementation, the interface isn't important:
public class MotorolaMC75WLAN : IWLANService
{
private readonly IMotorolaWLANAdapter _adapter;
private bool _disposed;

public MotorolaMC75WLAN(IMotorolaWLANAdapter adapter)
{
_adapter = adapter;
}


public void Enable()
{
if (!_adapter.IsEnabled)
{
_adapter.Enable();
_adapter.PowerStatusChanged += OnPowerStatusChanged;
}
}

private void OnPowerStatusChanged(object sender, PowerStatusChangedEventArgs e)
{
//raise events to interested parties....perhaps using some sort of event aggregator
}

public void Disable()
{
if (_adapter.IsEnabled)
{
_adapter.Disable();
_adapter.PowerStatusChanged -= OnPowerStatusChanged;
}
}

public bool IsEnabled
{
get { return _adapter.IsEnabled; }
}

public void RenewDHCP()
{
_adapter.RenewDHCP();
}

public void Connect()
{
_adapter.Connect();
}


#region IDisposable Members

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

#endregion

private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
Disable();
_adapter.Dispose();
}
_disposed = true;
}
}
}
So that is our high level implementation that can be easily extended and contains no low-level mobile code. The adapter in this case is injected and can be anything we want it to be which allows us to test this class easily on the desktop.

The next thing is our actual adapter. The interface for the adapter looks like this
public interface IMotorolaWLANAdapter : IDisposable
{
void Enable();

void Disable();

bool IsEnabled { get; }

void RenewDHCP();

event EventHandler PowerStatusChanged;

void Connect();
}
The implementation is low-level that talks directly to the WLAN class (Fusion API). I'm not going to include a typical implementation for this but an example Enable() method might look like the following:
public void Enable()
{
if (IsEnabled)
return;

Symbol.Fusion.WLAN.WLAN command = null;
try
{
command = new Symbol.Fusion.WLAN.WLAN(FusionAccessType.COMMAND_MODE);
command.Adapters[0].PowerState = Adapter.PowerStates.ON;
}
catch (OperationFailureException ex)
{
//do something...
}
finally
{
if (command != null)
command.Dispose();
}
}
It simply enables the first adapter it can find. Non-of the real adapter is testable.

So if this were a device test project, we'd have to mock out the adapter by hand, then pass it in to the constructor to the MotorolaMC75WLAN class. But as we have decided to use a desktop test project we can use any mocking framework that we choose which enables us free of having to do the laborious hand mocking work.

You can download Rhino Mocks from here if you don't already have it: http://www.ayende.com/projects/rhino-mocks/downloads.aspx. Download the zip and extract it somewhere on your hard disk, then add a reference the Rhino Mocks assembly in your test project - there is only one dll you need to reference.



Add a
using Rhino.Mocks;
to your test class. Now if you wanted to mock out the adapter Enable method, in Rhino Mocks world it could look like the following using the newer triple A syntax:
[TestMethod]
public void ShouldEnableWLAN()
{
IMotorolaWLANAdapter _mockWLANAdapter = MockRepository.GenerateMock<IMotorolaWLANAdapter>();
IWLANService _wlan = new MotorolaMC75WLAN(_mockWLANAdapter);

_mockWLANAdapter
.Expect(x => x.Enable())
.Repeat.Once();

_wlan.Enable();

_mockWLANAdapter.VerifyAllExpectations();
}
So the only actual thing we are testing here is the real MC75 WLAN class and not the adapter. The code is fairly easy to read. We are simply telling Rhino Mocks to expect exactly 1 call to Enable() when _wlan.Enable() is called. The thing to bear in mind here is to ensure your actual adapter doesn't contain bugs as we can't automate those tests on the build server.

There you have it, a true mocking framework put at use in the mobile space.

If you want to learn more about mocking frameworks, use Google, there are millions of articles out there on the subject.

3 comments:

Bronumski said...

Hi Simon,

Over here we are leaning more on the stubbing side of Rhino mocks and just using your test example we would re write it as:

[TestMethod]
public void ShouldEnableWLAN()
{
IMotorolaWLANAdapter _mockWLANAdapter = MockRepository.GenerateStub();

IWLANService _wlan = new MotorolaMC75WLAN(_mockWLANAdapter);

_wlan.Enable();

_mockWLANAdapter.AssertWasCalled(x => x.Enable(), y => y.Repeat.Once());
}

Simon Hart said...

That is a strange way to make use of stubs. Normally with a stub you return something. With Mocks you assert. That is the main difference. But either work I guess. I suppose I would be confused with your test above as to why you stubbed it and not mocked it.

Simon

Matt Davis (bronumski) said...

It could have been a mock but with Rhino there is no need if you are just asserting that something was called. Your example was testing that Enabled was called nothing else so it was just a simple Arrange Act Assert.

Had you wanted to check that a b and c were called then I would use the expect and verify syntax.

Also have a look at NSubstitute, I've been playing around with it for a bit.
http://nsubstitute.github.com/