J'ai du mal à déclencher un événement dans un test unitaire en utilisant VB.NET et NSubstitute. L'interface moquée définit un événement :

Event BlockOfVehiclesProcessed(source As Object, stats As ProcessingStats)

La classe testée enregistre un gestionnaire pour l'événement. Dans le test unitaire, je souhaite déclencher l'événement afin que le gestionnaire de la classe testée soit invoqué. Sur la base de la documentation NSubstitute (tous en C#, malheureusement) et de diverses réponses sur Stackoverflow, etc., j'ai essayé diverses permutations de ceci :

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

Mais je n'ai rien trouvé qui compile. Un message d'erreur :

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

J'ai essayé de passer 0 args et 2 args à EventWith(), j'ai essayé de spécifier le type arg explicitement pour EventWith(), et j'ai essayé Raise.Event(), mais je ne trouve pas la séquence magique pour rendre le compilateur heureux. Quelqu'un a-t-il un exemple de test unitaire VB fonctionnel qui déclenche un événement ?

0
Mike Woinoski 6 oct. 2018 à 01:49

1 réponse

Meilleure réponse

Ensuite, le problème est que NSubstitute ne prend pas en charge les types de gestionnaires d'événements anonymes créés par vb.net lorsqu'un événement est déclaré sans type de gestionnaire d'événements explicitement fourni.

Si l'utilisation de NSubstitute est obligatoire (et comme réponse à une question), la déclaration d'un événement du type de gestionnaire d'événement explicitement fourni résoudra votre problème.

' 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

Une autre approche consiste à créer sa propre fausse implémentation d'interface avec un événement. J'ai trouvé cette approche bien meilleure, uniquement parce que vous n'avez pas besoin de modifier votre code de production, car certains frameworks de test ne sont pas capables de prendre en charge les fonctionnalités du langage vb.net.

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
2
Mike Woinoski 8 oct. 2018 à 22:58