0

I'm having trouble raising an event in a unit test using VB.NET and NSubstitute. The interface being mocked defines an event:

Event BlockOfVehiclesProcessed(source As Object, stats As ProcessingStats)

The class under test registers a handler for the event. In the unit test, I want to raise the event so the handler in the class under test gets invoked. Based on the NSubstitute documentation (all C#, unfortunately) and various answers on Stackoverflow, etc., I tried various permutations of this:

AddHandler mock.BlockOfVehiclesProcessed, Raise.EventWith(New ProcessingStats(50))

But I haven't found anything that compiles. One error message:

Value of type 'EventHandlerWrapper(...)' cannot be converted to '...BlockOfVehiclesProcessedEventHandler'

I tried passing 0 args and 2 args to EventWith(), I tried specifying the type arg explicitly for EventWith(), and I tried Raise.Event(), but I can't find the magic sequence to make the compiler happy. Does anyone have an example of a working VB unit test that raises an event?

Mike Woinoski
  • 3,123
  • 1
  • 16
  • 15

1 Answers1

2

Then problem is that NSubstitute doesn't support anonymous event handler types created by vb.net when event declared without explicitly provided event handler type.

If use of NSubstitute is mandatory (and as an answer for question) declaring event of explicitly provided event handler type will solve your problem.

' Example with Action as event handler type
Public Interface IVehicleProcessor
    Event BlockOfVehiclesProcessed As Action(Of Object, String) 
End Interface

Public Class System
    Private ReadOnly _processor As IVehicleProcessor
    Public Property ProcessedStats As String

    Public Sub New(IVehicleProcessor processor)
        _processor = processor
        AddHandler _processor.BlockOfVehiclesProcessed, Sub(sender, stats) ProcessedStats = stats
    End Sub
End System

' Test
Public Sub ShouldNotifyProcessedStats()
    Dim fakeProcessor = Substitute.For(Of IVehicleProcessor)
    Dim system As New System(fakeProcessor)

    ' Raise an event with known event handler type
    AddHandler fakeProcessor.BlockOfVehiclesProcessed, 
        Raise.Event(Of Action(Of Object, String))(fakeProcessor, "test-stats")

    system.ProcessedStats.Should().Be("test-stats") ' Pass
End Sub

Another approach is to create own fake implementation of interface with an event. I found this approach much better, only because you don't need to change your production code, because some testing framework not able to support vb.net language features.

Public Class FakeVehicleProcessor
    Implements IVehicleProcessor

    Public Event BlockOfVehiclesProcessed(source As Object, stats As String) Implements IVehicleProcessor.BlockOfVehiclesProcessed

    ' Use this method to raise an event with required arguments
    Public Sub RaiseEventWith(stats As String)
        RaiseEvent BlockOfVehiclesProcessed(Me, stats)
    End Sub
End Class

' Test
Public Sub ShouldNotifyProcessedStats()
    Dim fakeProcessor As New FakeVehicleProcessor()
    Dim system As New System(fakeProcessor)

    fakeProcessor.RaiseEventWith("test-stats")

    system.ProcessedStats.Should().Be("test-stats") ' Pass
End Sub
Mike Woinoski
  • 3,123
  • 1
  • 16
  • 15
Fabio
  • 31,528
  • 4
  • 33
  • 72
  • Excellent! As you said, your second approach has some significant advantages, but in my case it was easier to use the NSubstitute strategy because of some quirks of the dependency's base class. But your suggestions did the trick: now my code is at 100% coverage. Thanks! – Mike Woinoski Oct 08 '18 at 19:20