3

I am playing around with a socket server and try to work according to the Test Driven Development pattern.

There's a working test for the socket creation method, but my test hangs due to the fact that I have a ManualResetEvent.WaitOne() so that my socket waits for a connection before creating another one.

Here's the code block to test:

ManualResetEvent allDone = new ManualResetEvent(false);
public void StartListening()
{
    allDone.Reset();

    //Inform on the console that the socket is ready
    Console.WriteLine("Waiting for a connection...");

    /* Waits for a connection and accepts it. AcceptCallback is called and the actual
     * socket passed as AsyncResult
     */
    listener.BeginAcceptTcpClient(
        new AsyncCallback(AcceptCallback),
        listener);

    allDone.WaitOne();
}

And here's the test method which hangs at "networkChannel.StartListening();

[TestMethod]
public void NetworkChannel_IsAcceptingConnectionsAsynchronously()
{
    ITcpClient client = MockRepository.GenerateMock<ITcpClient>();
    ITcpListener listener = MockRepository.GenerateMock<ITcpListener>();
    IAsyncResult asyncResult = MockRepository.GenerateMock<IAsyncResult>();

    listener.Expect(x => x.BeginAcceptTcpClient(null, null)).IgnoreArguments().Return(asyncResult);
    listener.Expect(x => x.EndAcceptTcpClient(asyncResult)).Return(client);

    NetworkChannel networkChannel = new NetworkChannel(listener);

    networkChannel.StartListening();
    //Some more work... fake callback etc., verfify expectations,...
}

If I comment all the manualresetevents, the test passes just fine but this cannot be the solution because the server tries to create duplicate sockets continuously ;-)

Any hints for me? Would be very much appreciated!

Regards, Martin

4

2 に答える 2

0

編集:

ここでの微妙な点は、StartListeningメソッドがブロックされることです。そのため、先に進む前にメソッドが終了するのを待つことはできないため、元のアプローチは機能しませんNetworkChannel

これに対する解決策は、チャネルが起動したときに何らかの通知を提供するようにすることです。いくつかのオプションを考えることができます:

以下を与えるNetworkChannel

public delegate void OnStartupComplete();
public event OnStartupComplete StartupComplete;

StartListening直前のメソッドでallDone.WaitOne();:

if (this.StartupComplete != null)
{
    StartupComplete();
}

イベントがセットアップされる場所:

ManualResetEvent resetEvent = new ManualResetEvent(false);
NetworkChannel networkChannel = new NetworkChannel();
networkChannel.StartupComplete += () => { resetEvent.Set(); };
networkChannel.StartListening();

resetEvent.WaitOne();

編集2:

StartListeningこのアプローチ (および以下のアプローチ) には、呼び出しのブロックにより、その後の実行が妨げられるという問題が残っていresetEvent.WaitOne();ます。あなたの最善の策は、StartListeningそのコードを別のスレッドで実行することだと思います。イベントと組み合わせると、StartupComplete必要なものを取得する必要があります。つまり、ネットワーク チャネルがリッスンしているときに通知し、リッスンresetEventするまで何もしないようにします。

別のスレッドの使用は、単体テストの観点からは望ましくないかもしれませんが、設計の観点からは望ましいと思います。このStartListeningようにブロックすると、単体テストを超えたときに不便になる可能性が高くなります。

StartListeningそのために、メソッドで次のようなことを行うことができます。

public void StartListening()
{
    ThreadPool.QueueUserWorkItem((object state) => 
    {
        allDone.Reset();

        //Inform on the console that the socket is ready
        Console.WriteLine("Waiting for a connection...");

        /* Waits for a connection and accepts it. AcceptCallback is called and the
        * actual socket passed as AsyncResult
        */
        listener.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), listener);

        if (this.StartupComplete != null)
        {
            StartupComplete();
        }

        allDone.WaitOne();
    });
}

あるいは、 を引数としてStartListening取り、 の前にそのメソッドを呼び出すようにメソッドを単純に変更することもできます。これを次のように設定します。ManualResetEventSetallDone.WaitOne();

ManualResetEvent resetEvent = new ManualResetEvent(false);
NetworkChannel networkChannel = new NetworkChannel();
networkChannel.StartListening(resetEvent);

resetEvent.WaitOne();
于 2012-11-08T07:38:19.907 に答える
0

問題は、何を正確にテストしたいのかということです。

StartListeningメソッドが を呼び出すことだけをテストが想定している場合、最も簡単な解決策は、ノンブロッキング モックを使用してリスナーで行ったように、SUT にlistener.BeginAcceptTcpClient同期オブジェクト ( ) を挿入してモックすることです。ManualResetEvent

ブロッキングをテストする場合は、テストで別のスレッドを開始してから、安全な待機時間 (2 秒としましょう) の後ManualResetEventを通知する必要があります。グジェゴシュはコメントした。listener.BeginAcceptTcpClient

スレッドと単体テストはやや複雑になる可能性があります。

于 2012-11-08T08:38:24.747 に答える