collectionsmockingxunitenumerationnsubstitute

Collection.Count shows zero even though Collection.GetEnumerator.Current contains a reference after MoveNext()


Note: An answer in either VB.NET or C# will be fine. I have no preference for this Q&A.

I'm trying to test my networking code and I'm running into a very strange problem: the GatewayAddresses collection on my mocked NetworkInterface always returns a Count of 0, even though its Enumerator's internal list clearly contains an item:

Debug Watch

I inserted this debugging code just before the assertions, and it works as expected:

Dim oEnumerator2 As IEnumerator(Of GatewayIPAddressInformation) = oGatewayAddresses.GetEnumerator

While oEnumerator2.MoveNext()
  Dim oCurrentGatewayInfo As GatewayIPAddressInformation = oEnumerator2.Current
  Me.Output.WriteLine($"Gateway IP Address: {oCurrentGatewayInfo.Address}")
End While

The string Gateway IP Address: 192.168.1.1 is written to the test results.

This assertion also succeeds:

Dim lMoved = oGatewayAddresses.GetEnumerator.MoveNext
oGatewayAddresses.GetEnumerator.Current.Address.Should.Be(oIPAddress)

But no matter what I try, the collection count always returns zero.

What could be the problem? My code is below.

--EDIT--

The problem lies either somewhere in my mocking setup or in NSubstitute itself. I prefer to consider the former.

Under this slight modification, the test passes:

'oNicService = Substitute.For(Of INicService)
oNicService = New NicService

' The actual function:
' Public Function GetActiveNetworkInterfaces() As IEnumerable(Of NetworkInterface) Implements INicService.GetActiveNetworkInterfaces
'   Return NetworkInterface.GetAllNetworkInterfaces.Where(Function(Nic) Nic.OperationalStatus = OperationalStatus.Up)
' End Function

' Act
oActiveNics = oNicService.GetActiveNetworkInterfaces
oGatewayAddresses = oActiveNics.First.GetIPProperties.GatewayAddresses

' Assert
oGatewayAddresses.Should.NotBeNull()
oGatewayAddresses.Should.HaveCount(1) ' <-- This succeeds

How should I adjust my mocking setup in order to get this to work?


Service

Public Interface INicService
  Function GetActiveNetworkInterfaces() As IEnumerable(Of NetworkInterface)
End Interface

Test

<Fact>
Public Sub GetActiveNetworkInterfaces_Should_Return_Correct_Gateway_Address()
  ' Arrange
  Dim oMockGatewayAddresses As GatewayIPAddressInformationCollection
  Dim oGatewayAddresses As GatewayIPAddressInformationCollection
  Dim oMockGatewayInfo As GatewayIPAddressInformation
  Dim oGatewayList As List(Of GatewayIPAddressInformation)
  Dim oEnumerator As IEnumerator(Of GatewayIPAddressInformation)
  Dim oActiveNics As IEnumerable(Of NetworkInterface)
  Dim oNicService As INicService
  Dim oIPAddress As IPAddress
  Dim oMockNic1 As NetworkInterface

  oMockGatewayAddresses = Substitute.For(Of GatewayIPAddressInformationCollection)
  oMockGatewayInfo = Substitute.For(Of GatewayIPAddressInformation)
  oIPAddress = IPAddress.Parse("192.168.1.1")
  oMockNic1 = Substitute.For(Of NetworkInterface)

  ' Mock GatewayIPAddressInformation
  oMockGatewayInfo.Address.Returns(oIPAddress)
  oMockNic1.OperationalStatus.Returns(OperationalStatus.Up)

  ' Mock GatewayIPAddressInformationCollection
  oGatewayList = New List(Of GatewayIPAddressInformation) From {oMockGatewayInfo}

  ' Create an enumerator that implements IEnumerator(Of GatewayIPAddressInformation)
  oEnumerator = oGatewayList.GetEnumerator

  ' Configure the mock to return the enumerator
  oMockGatewayAddresses.GetEnumerator.Returns(oEnumerator)

  ' Configure GetIPProperties.GatewayAddresses to return the mocked collection
  oMockNic1.GetIPProperties.GatewayAddresses.Returns(oMockGatewayAddresses)

  oNicService = Substitute.For(Of INicService)
  oNicService.GetActiveNetworkInterfaces.Returns({oMockNic1})

  ' Act
  oActiveNics = oNicService.GetActiveNetworkInterfaces
  oGatewayAddresses = oActiveNics.First.GetIPProperties.GatewayAddresses

  ' Assert
  oGatewayAddresses.Should.NotBeNull()
  oGatewayAddresses.Should.HaveCount(1) ' <-- Fails here
  oGatewayAddresses.First.Address.Should.Be(oIPAddress)
End Sub

Solution

  • ChatGPT to the rescue. (That thing's been getting me out of quite a few scrapes lately.)

    OK, it turns out that I was mocking the wrong things and not mocking the right things.

    Here's the updated test that passes:

    <Fact>
    Public Sub GetActiveNetworkInterfaces_Should_Return_Single_NetworkInterface_With_ExpectedGateway()
      Dim oActiveNetworkInterfaces As IEnumerable(Of NetworkInterface)
      Dim oExpectedGatewayAddress As IPAddress
      Dim oGatewayCollection As GatewayIPAddressInformationCollection
      Dim oGatewayAddresses As List(Of IPAddress)
      Dim oIpProperties As IPInterfaceProperties
      Dim oGatewayList As List(Of GatewayIPAddressInformation)
      Dim oGatewayInfo As GatewayIPAddressInformation
      Dim oNicService As INicService
      Dim oNics As List(Of NetworkInterface)
      Dim oNic As NetworkInterface
    
      ' Arrange
      oGatewayCollection = Substitute.For(Of GatewayIPAddressInformationCollection)
      oIpProperties = Substitute.For(Of IPInterfaceProperties)
      oGatewayInfo = Substitute.For(Of GatewayIPAddressInformation)
      oNicService = Substitute.For(Of INicService)
      oNic = Substitute.For(Of NetworkInterface)
    
      oNic.Name.Returns("Ethernet")
      oNic.OperationalStatus.Returns(OperationalStatus.Up)
    
      oExpectedGatewayAddress = IPAddress.Parse("192.168.1.254")
      oGatewayInfo.Address.Returns(oExpectedGatewayAddress)
    
      oGatewayList = New List(Of GatewayIPAddressInformation) From {oGatewayInfo}
      oGatewayCollection.GetEnumerator.Returns(oGatewayList.GetEnumerator)
      oGatewayCollection.Count.Returns(oGatewayList.Count)
    
      oIpProperties.GatewayAddresses.Returns(oGatewayCollection)
      oNic.GetIPProperties.Returns(oIpProperties)
    
      oNics = New List(Of NetworkInterface) From {oNic}
      oNicService.GetActiveNetworkInterfaces.Returns(oNics)
    
      ' Act
      oActiveNetworkInterfaces = oNicService.GetActiveNetworkInterfaces
    
      ' Assert
      oActiveNetworkInterfaces.Should.ContainSingle()
    
      oGatewayAddresses = oActiveNetworkInterfaces.
        Single.
        GetIPProperties.
        GatewayAddresses.
        Select(Function(Gateway)
                  Return Gateway.Address
                End Function).ToList
    
      oGatewayAddresses.Should.ContainSingle()
      oGatewayAddresses.Single.Should.Be(oExpectedGatewayAddress)
    End Sub