web-dev-qa-db-ja.com

C#Eventhandlerサブスクリプションのテスト

一部のイベントが特定の時点でサブスクライブまたはサブスクライブ解除されたことをユニットテストで確認できるようにしたいと思います。現在、2つのオプションが表示されます。

VerifiableEventHandler

各クライアントは、共通の+=および-=表記を使用してサブスクライブします。検証可能なイベントハンドラーを持つ各クラスは、GetInvocationList()を使用してイベントハンドラーをクエリできるIsSubscribe()を実装する必要があります。

public interface IVerifiableEventHandler<T> 
{
    event EventHandler<T> EventHandler;
    bool IsSubscribed(Delegate d);
}

// impl
public bool IsSubscribed(Delegate d)
{
    return EventHandler.GetInvocationList().Contains(d);
}

カスタムサブスクリプションインターフェイス

実際のC#イベントハンドラは非表示になり、クライアントはサブスクライブ/サブスクライブ解除機能を使用してのみサブスクライブできます。

public interface IEventSource<T>
{
    void Subscribe(EventHandler<T> func);
    void Unsubscribe(EventHandler<T> func);
    void Raise(object sender, T event_args);
}

長所と短所

私にとって、最初のアプローチの利点は、最小の驚きの原則に違反しないことです。 .Netプログラマは、イベントが+=表記を使用してサブスクライブされることを期待します。

2番目のアプローチの利点は、関数が特別なものではなく、イベントハンドラーを含むクラスがモックされている場合に簡単に検証できるため、テストが非常に簡単なことです。

この問題を解決するより良い方法はありますか?そうでない場合、2つのバリアントのどちらを優先する必要がありますか。また、それはなぜですか

7
Wilbert

3番目のオプションがあります。イベントでオブジェクトをモックし、動作テストを使用して、モックのサブスクリプションを特定のポイントで確認します。これを可能にするモックフレームワークがいくつかあります。

public interface INeedToBeMocked
{
    public event EventHandler EventRaised;
}

NSubstitute

var mockedItem = Substitute.For<INeedToBeMocked>();
mockedItem.Received().EventRaised += Arg.Any<EventHandler>();

Rhino Mocks

var mockedItem = MockRepository.GenerateMock<INeedToBeMocked>();
mockedItem.AssertWasCalled(x => x.EventRaised += Arg.Is.Anything);
7
Dan Lyons

この問題を解決するより良い方法はありますか?

はい!

C#では、次のことが可能です オーバーライド the +=および-=構文。 Moqのような一般的なモックフレームワークでの供給がどれほど簡単かはわかりませんが、インターフェイスのサブスクライブおよびサブスクライブ解除メソッドへのフックを持つ独自の偽のオブジェクトを作成するのは簡単です。

    private Action foo = () => {};
    public event Action Foo
    {
        add
        {
            Console.WriteLine("Subscribe called with value: {0}", value);
            foo += value;
        }

        remove
        {
            Console.WriteLine("Unsubscribe called with value: {0}", value);
            foo -= value;
        }
    }

(プロパティでもっと面白いことをしたいと思うかもしれませんが)

3
Telastyn