Come testare gli eventi

Testare che degli eventi siano stato effettivamente lanciati in C# non è immediato.

Tipicamente è possibile testare che un evento venga lanciato aspettando un ManualResetEvent che viene settato in un listener dell’evento stesso.

Questo metodo funziona ma risulta un po’ macchinoso; Fluent Assertion risolve il problema fornendo dei metodi comodi per testare il tutto.

Fluent Assertion

Fluent Assertion permette di testare che un determinato evento venga lanciato in modo semplice.

Per prima cosa è necessario comunicare a Fluent Assertion che voglio monitorare l’oggetto utilizzando il l’extension Monitor();

using var monitoredClass = _myClass.Monitor();

e successivamente è possibile utilizzare il check Should().Raise("EventName") in questo modo:

monitoredClass.Should().Raise(nameof(myClass.MyCustomEvent));

E’ possibile anche aggiungere dei check sul sender e sugli args in questo modo:

monitoredClass.Should()
            .Raise(nameof(myClass.MyCustomEvent))
            .WithSender(subject)
            .WithArgs<PropertyChangedEventArgs>(args => args.PropertyName == "SomeProperty");

Esempio completo

Di seguito un esempio completo di una classe di test per gli eventi.

using System;
using FluentAssertions;
using Xunit;

namespace Tests;

public class CustomEventArgs : EventArgs
{
    public CustomEventArgs(string param)
    {
        Param = param;
    }

    public string Param { get; }
}

public class ClassToTest
{
    public void MethodThatRaisesEvent()
    {
        // Do something
        RaiseMyCustomEventEvent();
        // Do something else
    }

    public void MethodThatRaisesEventWithArgs(string arg)
    {
        // Do something
        RaiseMyCustomEventWithArgsEvent(arg);
        // Do something else
    }

    public static void MethodThatDoNotRaiseEvent()
    {
        // Do nothing
    }

    #region MyCustomEvent

    public delegate void MyCustomEventEventHandler(object sender, EventArgs e);

    public event MyCustomEventEventHandler MyCustomEvent;

    private void RaiseMyCustomEventEvent()
    {
        MyCustomEvent?.Invoke(this, EventArgs.Empty);
    }

    #endregion

    #region MyCustomEventWithArgs

    public delegate void MyCustomEventWithArgsEventHandler(object sender, CustomEventArgs e);

    public event MyCustomEventWithArgsEventHandler MyCustomEventWithArgs;

    private void RaiseMyCustomEventWithArgsEvent(string arg)
    {
        MyCustomEventWithArgs?.Invoke(this, new CustomEventArgs(arg));
    }

    #endregion
}

public class TesterClass
{
    private readonly ClassToTest _myClass = new();

    [Fact]
    public void TestEventIsRaised()
    {
        using var monitoredClass = _myClass.Monitor();
        _myClass.MethodThatRaisesEvent();
        monitoredClass.Should().Raise(nameof(ClassToTest.MyCustomEvent));
    }

    [Fact]
    public void TestEventAndArgsIsRaised()
    {
        const string arg = "foo";
        using var monitoredClass = _myClass.Monitor();
        _myClass.MethodThatRaisesEventWithArgs(arg);
        monitoredClass.Should().Raise(nameof(ClassToTest.MyCustomEventWithArgs))
            .WithSender(_myClass)
            .WithArgs<CustomEventArgs>(args => args.Param == arg);
    }

    [Fact]
    public void TestEventIsNotRaised()
    {
        using var monitor = _myClass.Monitor();
        ClassToTest.MethodThatDoNotRaiseEvent();
        monitor.Should().NotRaise(nameof(ClassToTest.MyCustomEvent));
    }
}

Indice

Share
Ultimi articoli

Codice Pragmatico

Contatti

Per informazioni, dubbi o consulenze non esitate a contattarmi.

Lascia un messaggio

Ricevi le ultime news

Iscrivi alla newsletter

Solo articoli interessanti, promesso ;)