How can I mock IotHubServiceClient for testing purposes? (v2)

108 Views Asked by At

I have a class IotHubService that depends on the C# IoTHub SDK's IotHubServiceClient (v2 version of the SDK, currently in preview) to perform various queries on an IoTHub. I'm trying to improve the reliability of my class by writing some tests and mocking dependencies:

[Fact]
public async Task GetDeviceByIdAsync_ValidDeviceId_ReturnsDevice()
{
    // Arrange
    string deviceId = "testDeviceId";
    Device expectedDevice = new Device(deviceId);

    // Create a mock IotHubServiceClient
    Mock<IotHubServiceClient> mockIotHubServiceClient = new Mock<IotHubServiceClient>();
    mockIotHubServiceClient
            .Setup(c => c.Devices.GetAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(expectedDevice);

    // Create an instance of IoTHubService using the mock client
    IotHubService ioTHubService = new IotHubService(mockIotHubServiceClient.Object);

    // Act
    Device result = await ioTHubService.GetDeviceByIdAsync(deviceId);

    // Assert
    Assert.Equal(expectedDevice, result);
    mockIotHubServiceClient.Verify(c => c.Devices.GetAsync(deviceId, default(CancellationToken)), Times.Once);
}

However, I'm unsuccesful in mocking the IotHubServiceClient and receive the following error when running my test:

System.NotSupportedException : Invalid setup on a non-virtual (overridable in VB) member: mock => mock.Devices`

I understand that the underlying DevicesClient is sealed, but I was wondering if there is any way solve this problem?

Edit: What my IotHubService class looks like:

public class IotHubService : IIotHubService
    {
        protected readonly IotHubServiceClient _iotHubServiceClient;

        public IotHubService(string connectionString)
        {
            _iotHubServiceClient = new IotHubServiceClient(connectionString);
        }

        public IotHubService(IotHubServiceClient iotHubServiceClient)
        {
            _iotHubServiceClient = iotHubServiceClient;
        }

        public async Task<Device> GetDeviceByIdAsync(string deviceId)
        {
            if (string.IsNullOrEmpty(deviceId))
            {
                throw new ArgumentNullException(nameof(deviceId));
            }

            try
            {
                return await _iotHubServiceClient.Devices.GetAsync(deviceId);
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
1

There are 1 best solutions below

2
Sampath On

Using IotHubService with Azure IoT, I am able to mock and test.

  • Sample example with mock test and unit test.
public class IotHubServiceTests
{
    
    private const string ConnectionString = "  ";

    [Fact]
    public async Task CheckDeviceConnection_DeviceExists_ReturnsTrue()
    {
        
        var iotHubService = new IotHubService(ConnectionString);
        var deviceId = " ";

       
        Device device = await iotHubService.GetDeviceByIdAsync(deviceId);

       
        Assert.NotNull(device);
        Assert.Equal(deviceId, device.Id);
        Assert.True(device.ConnectionState == DeviceConnectionState.Connected);

        
        if (device != null && device.ConnectionState == DeviceConnectionState.Connected)
        {
            Console.WriteLine("CheckDeviceConnection_DeviceExists_ReturnsTrue: Passed");
        }
        else
        {
            Console.WriteLine("CheckDeviceConnection_DeviceExists_ReturnsTrue: Failed");
        }
    }

    [Fact]
    public async Task CheckDeviceConnection_DeviceNotFound_ReturnsFalse()
    {
        
        var iotHubService = new IotHubService(ConnectionString);
        var deviceId = "nonexistent_device";

        
        Device device = await iotHubService.GetDeviceByIdAsync(deviceId);

        
        Assert.Null(device);

        
        if (device == null)
        {
            Console.WriteLine("CheckDeviceConnection_DeviceNotFound_ReturnsFalse: Passed");
        }
        else
        {
            Console.WriteLine("CheckDeviceConnection_DeviceNotFound_ReturnsFalse: Failed");
        }
    }

    [Fact]
    public async Task CheckDeviceConnection_NullDeviceId_ThrowsArgumentNullException()
    {
       
        var iotHubService = new IotHubService(ConnectionString);
        string deviceId = null;

        
        await Assert.ThrowsAsync<ArgumentNullException>(() => iotHubService.GetDeviceByIdAsync(deviceId));

        
        Console.WriteLine("CheckDeviceConnection_NullDeviceId_ThrowsArgumentNullException: Passed");
    }
}

public class IotHubService
{
    private readonly RegistryManager _registryManager;

    public IotHubService(string connectionString)
    {
        _registryManager = RegistryManager.CreateFromConnectionString(connectionString);
    }

    public async Task<Device> GetDeviceByIdAsync(string deviceId)
    {
        if (string.IsNullOrEmpty(deviceId))
        {
            throw new ArgumentNullException(nameof(deviceId));
        }

        try
        {
            return await _registryManager.GetDeviceAsync(deviceId);
        }
        catch (DeviceNotFoundException)
        {
            
            return null;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}
static void Main(string[] args)
{
    Console.WriteLine("Running Xunit tests...");

   
    var assembly = typeof(Program).Assembly;
    var assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
    var result = Xunit.ConsoleClient.Program.Main(new[] { assemblyLocation });

    if (result == 0)
    {
        Console.WriteLine("Xunit tests passed.");
    }
    else
    {
        Console.WriteLine("Xunit tests failed.");
    }

    Console.WriteLine("Press any key to exit...");
    Console.ReadKey();
}

Output: enter image description here

With Mocking and Testing:

 [Fact]
    public async Task CheckDeviceConnection_DeviceExists_ReturnsTrue()
    {
        // Arrange
        var deviceId = "mytestdevice1";
        var mockRegistryManager = new Mock<RegistryManager>();
        mockRegistryManager.Setup(manager => manager.GetDeviceAsync(deviceId))
                           .ReturnsAsync(new Device(deviceId));

        var iotHubService = new IotHubService(ConnectionString, mockRegistryManager.Object);

        // Act
        Device device = await iotHubService.GetDeviceByIdAsync(deviceId);

        // Assert
        Assert.NotNull(device);
        Assert.Equal(deviceId, device.Id);
        Assert.True(device.ConnectionState == DeviceConnectionState.Connected);

        // Print the result of this test
        if (device != null && device.ConnectionState == DeviceConnectionState.Connected)
        {
            Console.WriteLine("CheckDeviceConnection_DeviceExists_ReturnsTrue: Passed");
        }
        else
        {
            Console.WriteLine("CheckDeviceConnection_DeviceExists_ReturnsTrue: Failed");
        }
    }