How can I do unit testing for BLE(Bluetooth Low Energy) in xamarin forms. I am testing methods which implements the BLE Gatt operation using Nunit . I am able to do unit testing for the viewmodel. But how will I test the singleton classes where the BLE gatt operation is going on. This is the Data transfer class which acts as intermediate.
public class DataTransfer:IManager
{
public ICharacteristic _characteristicsBLE;
private static readonly object padlock = new object(); //Using Padloc to make code thread safe
private static DataTransfer instance = null;
public string _result;
public IDevice _deviceBLE; //Contains information of BLE device
private DataTransfer()
{
}
//Instance of Singeleton class
public static DataTransfer Instance
{
get
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new DataTransfer();
}
}
}
return instance;
}
}
public async Task connect(ScanData scanData, CancellationTokenSource token)
{
try
{
await BleManager.Instance.BleState(scanData, token);
// await getCharacteristics(scanData, token);
}
catch(Exception ex)
{
await UserDialogs.Instance.AlertAsync("The data is incomplete scan again. Datatransfer");
}
}
public async Task getCharacteristics(ScanData scanData, CancellationTokenSource token)
{
try
{
await BleManager.Instance.GetCharacteristics(scanData);
}
catch (Exception ex)
{
await UserDialogs.Instance.AlertAsync("The data is incomplete scan again. Datatransfer");
}
}
}
This is the BLE manager class which has BLE gatt operations.
public class BleManager
{
private IBluetoothLE _bluetoothLe;
public IBluetoothService _bleState; //Reference for BluetoothService which has execution for turning ON bluetooth using Dependency service
public IDevice _device; //Contains information of BLE device
public IAdapter _adapter;
public static readonly Guid SERVICE_ID = Guid.Parse("00001805-0000-1000-8000-00805f9b34fb"); //Sevice ID of BLE GATT
public static readonly Guid CHARACTERISTIC_ID_WRITE = Guid.Parse("00002B0F-0000-1000-8000-00805f9b34fb"); //Characteristic ID of BLE GATT Write
public static readonly Guid CHARACTERISTIC_ID_READ = Guid.Parse("00002a2b-0000-1000-8000-00805f9b34fb");
public string concat { get; set; }
public string BLEresultnew { get; set; }
public ICharacteristic _characteristicsBLE;
public NativeDevice NativeDeviceAdd { get; private set; } //Class which contain name and address of BLE
public string BleDeviceName { get; private set; }
private const string SERVICE_NAME = "Current Time Service"; //Service Name of BLE GATT
private static readonly object padlock = new object(); //Using Padloc to make code thread safe
private static BleManager instance = null;
private BleManager()
{
}
//Instance of Singeleton class
public static BleManager Instance
{
get
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new BleManager();
}
}
}
return instance;
}
}
//Check if Bluetooth is ON or OFF. If Bluetooth is switched OFF in android then switch it ON.
public async Task BleState(ScanData scanData, CancellationTokenSource token)
{
_bluetoothLe = CrossBluetoothLE.Current;
if (_bluetoothLe.State == BluetoothState.Off)
{
if (Device.RuntimePlatform == Device.iOS)
{
await UserDialogs.Instance.AlertAsync("Switch On bluetooth");
}
else
{
_bleState = DependencyService.Get<IBluetoothService>();
_bleState.OpenBluetooth();
await ScanForDevices(scanData);
}
}
else
{
await ScanForDevices(scanData);
}
}
//Scan for BLE devices
private async Task ScanForDevices(ScanData scanData, ConnectParameters connectParameters = default, CancellationToken token = default)
{
try
{
_device = null;
_adapter = CrossBluetoothLE.Current.Adapter;
_adapter.ScanMode = ScanMode.LowLatency;
_adapter.ScanTimeout = 5000;
Device.BeginInvokeOnMainThread(async () =>
{
_adapter.DeviceDiscovered += async (s, a) =>
{
NativeDeviceAdd = DependencyService.Get<INativeDevice>().ConvertToNative(a.Device);
PropertyInfo propInfo = NativeDeviceAdd.GetType().GetProperty("Name");
BleDeviceName = (string)propInfo.GetValue(NativeDeviceAdd, null);
string substr = scanData.blename;
if (BleDeviceName == substr)
{
_device = a.Device;
await _adapter.StopScanningForDevicesAsync();
await ConnectForDevice(scanData);
}
};
});
await _adapter.StartScanningForDevicesAsync();
}
catch (DeviceConnectionException ex)
{
await UserDialogs.Instance.AlertAsync("Device got disconnected please scan again 1");
return;
}
catch (Exception ex)
{
await UserDialogs.Instance.AlertAsync("Device got disconnected please scan again.The CancellationTokenSource has been disposed");
return;
}
}
//If the device is selected the connection is done here
private async Task ConnectForDevice(ScanData scanData, ConnectParameters connectParameters = default, CancellationToken token = default)
{
try
{
_adapter.DeviceConnectionLost += async (s, a) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
Console.WriteLine("Device ConnectionLost : " + a.ErrorMessage);
await UserDialogs.Instance.AlertAsync("Device ConnectionLost :"+a.ErrorMessage);
var parameters2 = new ConnectParameters(forceBleTransport: true, autoConnect: true);
await _adapter.ConnectToDeviceAsync(_device, parameters2);
await GetCharacteristics(scanData);
});
};
_adapter.DeviceDisconnected += async (s, a) =>
{
Console.WriteLine("Device Disconnected : ");
await UserDialogs.Instance.AlertAsync("Device Disconnected :");
};
var parameters = new ConnectParameters(forceBleTransport: true,autoConnect:true);
await _adapter.ConnectToDeviceAsync(_device, parameters);
await GetCharacteristics(scanData);
}
catch (DeviceConnectionException ex)
{
Console.WriteLine("Exception while connecting and registering callbacks " + ex.Message);
await UserDialogs.Instance.AlertAsync("Device got disconnected please scan again 2" + ex.Message);
}
}
public async void disconnected()
{
await Application.Current.MainPage.Navigation.PopAsync();
return;
}
public async Task GetCharacteristics(ScanData scanData, ConnectParameters connectParameters = default, CancellationToken token = default)
{
try
{
concat = null;
var service = await _device.GetServiceAsync(SERVICE_ID);
var characteristicRead = await service.GetCharacteristicAsync(CHARACTERISTIC_ID_READ);
await ReadConfigData(characteristicRead);
}
catch (DeviceConnectionException ex)
{
await UserDialogs.Instance.AlertAsync("Device got disconnected please scan again 3");
}
catch (TaskCanceledException tce)
{
Console.WriteLine("Scan was cancelled");
}
}
private async void OnDeviceDisconnected(object sender, DeviceEventArgs e) //Is OK to use async void here
{
await UserDialogs.Instance.AlertAsync("Device got disconnected after scan");
}
private async void OnDeviceconnected(object sender, DeviceEventArgs e) //Is OK to use async void here
{
await UserDialogs.Instance.AlertAsync("Device got disconnected please scan again event");
}
public async Task WriteDataAsync(String data)
{
if (_characteristicsBLE != null)
{
try
{
byte[] senddata = Encoding.UTF8.GetBytes(data);
int start = 0;
while (start < senddata.Length)
{
int chunkLength = Math.Min(20, senddata.Length - start);
byte[] chunk = new byte[chunkLength];
Array.Copy(senddata, start, chunk, 0, chunkLength);
Device.BeginInvokeOnMainThread(async () =>
{
await _characteristicsBLE.WriteAsync(chunk);
});
start += 20;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
public async Task ReadConfigData(ICharacteristic characteristics)
{
if (characteristics != null)
{
try
{
await _adapter.ConnectToDeviceAsync(_device);
characteristics.ValueUpdated += (o, e) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
var bytes = e.Characteristic.Value;
BLEresultnew = System.Text.Encoding.UTF8.GetString(bytes);
Console.WriteLine(BLEresultnew);
concat += BLEresultnew;
});
};
await characteristics.StartUpdatesAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
I have no clue how can I test these singeleton classes. Is there an
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…