应该如何使 class 具有依赖关系的单元测试而不会膨胀?

How should one make a class with dependencies unit testable without bloat?

所以我有一个 class(库的一部分)可以促进双向 TCP 通信。其中一部分是接受传入连接。

我的实现包括使用 TCPListener 对象和多线程方法来接受连接。为了使其可测试,我决定创建一个 "INetworkListener" 接口,它只包含一个事件 "OnClientAccepted"。这样,我可以使用依赖注入来模拟 TCPListener 的虚假版本并避免多线程单元测试。

问题是我需要一种使用假 INetworkListener 来测试 class 的方法,但我希望我的用户 可以选择使用其他实现这个界面的。

这是一些精简的示例代码:

class TcpMessenger
{
    // Various properties

    private INetworkListener _tcpListener;

    public TcpMessenger(int port, string friendlyName) // This is the ONLY constructor I want available to users
    {
        ServerPort = port;
        FriendlyName = friendlyName;
        _isRunning = false;
        _tcpListener = new ConcreteExample(port); // This prevents unit testing because it opens threads and such
    }

    public TcpMessenger(int port, string friendlyName, INetworkListener listener) // I need this to test
    {
        ServerPort = port;
        FriendlyName = friendlyName;
        _isRunning = false;
        _tcpListener = listener; // No dependency here :)
    }
}

为什么我不能只保留两个构造函数?

我的图书馆就像一个门面。它使 TCP 通信更容易,但没有增加太多功能。因此,我的目标受众永远不需要注入这种依赖性。如果我不希望他们这样做,那么适当的设计会告诉我强制执行它。

为什么不直接将其作为面向 public 的 API 进行测试?集成测试!

我实际上是用我的库的以前版本做的,它非常耦合。结果很糟糕。我进行了十几个涉及多线程的测试,更糟糕的是,实际的套接字。单元测试不应该依赖于这样的外部因素。

你真的需要测试这个吗?

是的。该库具有高级功能,例如异常处理、错误报告、连接问题的故障保护以及维护双向连接的算法(意味着每个节点都是客户端和服务器)。这个功能可以在不打开实际套接字的情况下进行测试,所以我绝对想要那个。

我也希望单元测试是 public。这意味着任何人都可以获取源代码,运行 他们,并查看所有绿色复选标记。

结论性问题:

如何独立测试两个 classes,同时在现实场景中强制依赖?

您可以使用 InternalsVisibleTo:

使您的构造函数成为内部结构并将您的内部结构暴露给您的测试
[assembly: InternalsVisibleTo("YourNamespace.YourTests")]

参见:https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute(v=vs.110).aspx