如何对客户端网络代码进行单元测试?

4

我正在编写一段网络代码,用于监听TCP连接、解析传入的数据并触发相应的事件。为了避免阻塞应用程序的其他部分,监听和解析操作在后台工作线程中执行。当尝试对此代码进行单元测试时,会遇到一个问题:由于网络代码的工作量比单元测试更大,因此单元测试完成之前适配器没有机会触发事件,导致测试失败。

适配器类:

public class NetworkAdapter : NetworkAdapterBase //NetworkAdapterBase is just an abstract base class with event definitions and protected Raise... methods.
{
    //Fields removed for brevity.

    public NetworkAdapter(TcpClient tcpClient)
    {
        _tcpConnection = tcpClient;

        //Hook up event handlers for background worker.
        NetworkWorker.DoWork += NetworkWorker_DoWork;

        if (IsConnected)
        {
            //Start up background worker.
            NetworkWorker.RunWorkerAsync();
        }
    }

    private void NetworkWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        while (IsConnected)
        {
            //Listen for incoming data, parse, raise events...
        }
    }
}

尝试的测试代码:

[TestMethod]
public void _processes_network_data()
{
    bool newConfigurationReceived = false;

    var adapter = new NetworkAdapter(TestClient); //TestClient is just a TcpClient that is set up in a [TestInitialize] method.

    adapter.ConfigurationDataReceived += (sender, config) =>
    {
        newConfigurationReceived = true;
    };

    //Send fake byte packets to TestClient.

    Assert.IsTrue(newConfigurationReceived, "Results: Event not raised.");
}

我该如何尝试测试这种类型的事情?

谢谢,

詹姆斯

4个回答

6

首先,这不是一个严格的“单元测试”;你的测试依赖于具有副作用的架构层,例如传输网络数据包。这更像是集成测试。

话虽如此,你的单元测试可以休眠一定数量的毫秒,就像Tony所说的那样。你还可以尝试获取后台工作进程的句柄,并加入它,这将导致你的单元测试等待直到后台工作进程完成。


2
您可以等待一段超时时间,然后运行断言,如下所示:
//Send fake byte packets to TestClient
Thread.Sleep(TIMEOUT);
Assert.IsTrue(newConfigurationReceived, "Results: Event not raised.");

在这里,TIMEOUT是您希望等待的毫秒数。


最初(当我直接从后台工作线程调用事件时),我使用了类似的超时,这很有效。然而,从后台工作线程调用事件意味着处理程序也在工作线程上执行,在UI层中引起各种不良反应,因此我修改了代码,通过分派在主线程上引发事件,但这似乎破坏了测试中的超时方法。 - Moonshield

2

在我们的单元测试中,我们使用.NET 4的并行化库。您可以这样说:

Parallel.Invoke(() => Dosomething(arguments), () => DosomethingElse(arguments));

框架将负责将这些操作作为不同的线程生成,以一种最适合您正在处理的特定进程的方式执行它们,然后将它们连接起来,以便在它们全部完成之前不会执行下一条指令。

但是,看起来您可能没有直接访问线程。相反,您希望等待给定的回调方法被调用。您可以使用AutoResetEvent或ManualResetEvent来实现此目的。

请参见单元测试异步函数


谢谢,看起来很有趣。我会试着玩一下,看看效果如何。 - Moonshield

2
您可以使用一些超时,但通常情况下,超时时间应该是多少才能确保您的测试始终通过,但又不会使测试变得太慢呢?
我建议将解析代码单独测试。这可能是您最容易出错的地方,也是您最需要单元测试的地方。而且这很简单!
对于监听套接字的代码...好吧,您可能会在这里遇到错误...但是如果它只是将数据分派给一个函数/类,我不确定您是否真的需要测试它。如果您想非常彻底,例如客户端和服务器之间的连接断开,您将如何对其进行单元测试以确保您的类表现良好?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接