1

I am trying to develop some tests for a Windows 10 UWP application which uses Windows.Devices.Bluetooth.BluetoothLEDevice. I have a plain class that is part of my application which has a private BluetoothLEDevice type field.

class MyDevice
{
    private Windows.Devices.Bluetooth.BluetoothLEDevice BluetoothLEDevice;

    public string SomeProperty { get; set; }

    public MyDevice(Windows.Devices.Bluetooth.BluetoothLEDevice bluetoothLEDevice)
    {
        BluetoothLEDevice = bluetoothLEDevice;

        var characteristic = BluetoothLEDevice.GetGattService(...)
            .GetCharacteristics(...)
            .First();

        characteristic.ValueChanged += OnValueChanged;
    } 

    // TODO: Write tests for this method
    private OnValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
    {
        string message = Encoding.ASCII.GetString(args.CharacteristicValue.ToArray());

        // Parse `message`

        SomeProperty = parsed;
    }

    ...
}

The methods of said class use the events and methods of bluetoothLEDevice, some are private and others are public. How can I test the public methods of MyDevice?

I have tried something like this which I think could work, but I can see that it will take hundreds of lines of code and quite a few extra classes because I would need to implement a lot of stuff in FakeBluetoothLEDevice in orde for it to work properly.

I changed MyDevice to accept a wrapper instead and then create two implementations of the wrapper. One for testing, and the other for real use.

class MyDevice
{
    private MyApp.IBluetoothLEDeviceWrapper bluetoothLEDevice;
}

Then in my test I use the fake.

private void ValueChangedEventDataParsingTest()
{
    var device = new FakeBluetoothLEDevice();

    var myDevice = new MyDevice(device);

    device.InvokeValueChanged("this is the value for a fake ValueChangedEvent");

    Assert.Equals(probe.SomeProperty, "expected");
}

Are there any frameworks (available for UWP) that would help me achieve what I want? Or even a better approach that would save me some pain?

Erik Berkun-Drevnig
  • 2,306
  • 23
  • 38
  • How are you able to use a fake `BluetoothLEDevice` if that class is `sealed`? – Nkosi Mar 30 '17 at 21:25
  • Updated with further explanation – Erik Berkun-Drevnig Mar 30 '17 at 21:31
  • you can use a mocking framework like [Moq](https://github.com/Moq/moq4/wiki/Quickstart) and only configure/setup the methods/properties/events that you directly access when testing – Nkosi Mar 30 '17 at 21:33
  • I looked at Moq and it looks absolutely perfect, but it doesn't seem to support UWP applications. – Erik Berkun-Drevnig Mar 30 '17 at 21:34
  • http://stackoverflow.com/questions/33242211/mocking-framework-in-uwp-apps – Nkosi Mar 30 '17 at 21:35
  • Thanks for the replies. I saw that answer as well and gave simple stubs a try but it has the limitation of only being able to stub interfaces. I need to stub `BluetoothLEDevice`. – Erik Berkun-Drevnig Mar 30 '17 at 21:40
  • If you are using interface wrapper then there is no need to stub the concrete class. No need to test/stub code you do not control. MS would have tested it before release. – Nkosi Mar 30 '17 at 21:41
  • I thought the purpose of stubbing a class was so that I did not have to test it? Sorry, I am somewhat new to this. Basically I have a private event handler in the `MyDevice` class which handles the `ValueChanged` event coming from one of the GATT characteristics of `BluetoothLEDevice` that I want to test. I figured the best way would be to mock the BluetoothLEDevice so I can send an event with the test data to test that event handler. I am stubbing the MS code/dependencies of the code I have written, does that make sense? – Erik Berkun-Drevnig Mar 30 '17 at 21:48
  • You may need to better demonstrate that in your question with a [mcve]. based on what you've explained so far I am not totally grasping the problem. – Nkosi Mar 30 '17 at 21:50
  • Okay, I updated the example code with more details. I think that it made it more clear. Let me know if you think it needs anymore clarification. Thanks for the help so far. – Erik Berkun-Drevnig Mar 30 '17 at 22:04
  • What is the edition of Visual Studio that you are using? – zaitsman Mar 31 '17 at 09:08
  • Visual Studio 2017 – Erik Berkun-Drevnig Mar 31 '17 at 13:06

1 Answers1

2

In stead of focusing on implementation concerns focus on what functionality you want your abstraction to expose. Using your simplified example I was able to replicate it with some refactors to only interact with the desired functionality.

[TestClass]
public class DeviceTests {
    [TestMethod]
    public void _ValueChangedEventDataParsingTest() {
        //Arrange
        var message = "message";
        var expected = "expected";
        var device = new FakeBluetoothLEDevice(message, expected);
        var sut = new MyDevice(device);

        //Act
        device.InvokeValueChanged(message);

        //Assert
        Assert.AreEqual(expected, sut.SomeProperty);
    }

    public interface IBlueToothService {
        Action<string> ValueChangedHandler { get; set; }
    }

    public class FakeBluetoothLEDevice : IBlueToothService {
        private string message;
        private string parsed;

        public FakeBluetoothLEDevice(string message, string expected) {
            this.message = message;
            this.parsed = expected;
        }
        public Action<string> ValueChangedHandler { get; set; }

        public void InvokeValueChanged(string p) {
            var handler = ValueChangedHandler ?? delegate { };
            if (p == message) {
                ValueChangedHandler(parsed);
            }
        }
    }

    public class MyDevice {
        private IBlueToothService device;

        public string SomeProperty { get; set; }

        public MyDevice(IBlueToothService device) {
            this.device = device;
            device.ValueChangedHandler = handler;
        }

        private void handler(string parsedValue) {
            SomeProperty = parsedValue;
        }
    }
}

Use separation of concerns and move the heavy lifting of implementation concerns behind the actual implementations. It vastly simplifies the consumers of such functionality.

If the concern is to test he parsing functionality then abstract the out into its own concern as well. Don't have classes doing more than they need to (SRP)

private OnValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args) {
    string message = Encoding.ASCII.GetString(args.CharacteristicValue.ToArray());

    // Parse `message`
    var parsed = parsingServce.Parse(message);

    SomeProperty = parsed;
}

That way the parsing service implementation will only need to be tested for ts core functionality.

But from an abstraction perspective the parser is not needed as a dependency when testing higher level functionality.

I advise reviewing the current design and refactoring it to be more SOLID.

Nkosi
  • 235,767
  • 35
  • 427
  • 472