C#中的TDD测试服务/后台代码

本文关键字:后台 代码 服务 测试 中的 TDD | 更新日期: 2023-09-27 18:00:09

因此,我正在编写一个小型异步服务器,该服务器每次从网络接收消息时都会调用委托,而我在如何测试该类方面遇到了问题。我是测试新手,我已经在JavaScript中使用TDD进行了第一次测试,现在我正在尝试使用TDD C#。

我从写第一个测试开始,我想象我想要一些静态工厂来构建对象,这样,如果参数无效或为空,我可以在构建对象之前检查它们,所以我写了这个,并进行了第一个红-绿重构循环。

[TestMethod]
public void CreateShouldCreateAnInstanceWithValidParameters()
{
    MessageReceivedCallback callback = delegate(Message message, TcpClient sender) {};
    AsyncTcpServer server = AsyncTcpServer.Create(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 40400), callback);
    Assert.IsTrue(server != null && server is AsyncTcpServer);
}

我认为到目前为止还不错(afaik)。

现在,我想我需要一些Start()和Stop()方法来在后台启动这个服务器,所以它们将是同步方法,首先启动一些代码的线程,这些代码将从TcpListener读取,直到我调用Stop()。

现在问题来了,因为出现了很多问题,而我似乎没有找到解决这些问题的最佳方案(或者只是一个解决方案):

  • 我测试Start()方法的目的是什么?我要检查它是否创建了线程吗?或者,以某些属性(如IsRunning)的形式提供服务器的状态,并确保在Start()被称为false之前,Start()为true之后,Stop()为false之后,这会是一个更好的主意吗?

  • 如果我测试IsRunning值,如果我只实现了必要的代码(正如几本书和教程中所说,只实现了使测试通过所需的代码),就可以在调用后更改值,而不创建线程,这会有点假吗?我认为,如果我创建线程,它将是未经测试的代码,因为我找不到合适的方法来测试线程是否已创建(我可以将类的线程成员公开为公共,但我认为这会很难看,而且不是一个封装得很好的类)。

那么,我做错了什么?我该怎么办?

C#中的TDD测试服务/后台代码

假设您试图遵循TDD,当您开始编写测试和代码以满足它们时,您的设计可能会发生变化。你会发现有些东西很难写测试。这可能是因为这是一件很难测试的事情,也可能表明你试图做得太多了。

如果我测试IsRunning值,如果我只实现了必要的代码就通过了,它会有点假吗

编写尽可能少的代码以使测试通过的目的是确保您不会编写未进行测试的代码。这有助于鼓励你重构代码,以提高设计的可测试性,从而能够编写测试,让代码做你想做的事情。

看看你目前的情况,感觉你在AsyncTcpServer课上投入了太多。据我所知,它将负责几件事,包括管理TCP连接和运行单独的执行线程。这是两个不同的问题,试图在同一个班上测试它们,你会让自己的生活变得艰难。

您需要尝试将关注点分开,并对其进行单独测试。因此,例如,使用可以创建一个ThreadRunner,类似于以下内容:

class ThreadedRunner {
    public ThreadedRunner(IRunnable runnable) {
      // store runnable
    }
    public void Start() {
      // start thread to execute runnable.ThreadFunction
    }
    public void Stop() {
      // Send stop message, block until done
    }
}

对于接口IRunnable:

interface IRunnable {
    void ThreadFunction();
    void Stop();
}

然后,您可以使用IRunnable的测试实现来测试ThreadRunner。还会有其他测试,但最终你可能会得到这样的结果:

class TestableRunnable : IRunnable {
    public bool IsRunning {get;set;}
    public void ThreadFunction() {
        // Sleep
        // IsRunning=true
        // WaitOnShutdownMutex
        // IsRunning=false
    }
    public void Stop() {
       // Set shutdown mutex
    }
}

TestRunnableStartsThreadAndStops() {
    // Create Testable... Pass it to Runner
    // Validate testable isn't running
    // tell runner to start, 
    // Validate testable isn't running (it should be sleeping if it's in another thread)
    // sleep to give it time to start thread
    // validate it's running
    // call stop
    // validate that it blocked until after the state is stopped
}

然后,您可以继续编写一个可运行的线程处理部分(它所做的可能只是将该进程的不同元素委托给其他类,这些类反过来将具有较小的功能部分,您可以对其进行测试)。

TDD的一个关键方面是,它让你从客户端和测试的角度来看待你的代码,试图鼓励你在重构过程中让你的类变得可用和可测试。