如何使一个具有依赖性的类单元可测试而不会膨胀?

时间:2017-07-09 01:16:03

标签: c# unit-testing dependency-injection tdd

所以我有一个类(库的一部分),便于双向TCP通信。部分原因是接受传入连接。

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

问题是我需要一种方法来使用假的INetworkListener测试该类,但我希望我的用户可以选择使用此接口的其他实现。

以下是一些精简的示例代码:

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通信更容易,但没有增加太多功能。因此,我的目标受众永远不需要注入这种依赖。如果我不想让他们这样做,那么正确的设计会告诉我强制执行它。

为什么不将其作为面向公众的API进行测试?集成测试!

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

你真的需要测试吗?

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

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

结束语:

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

1 个答案:

答案 0 :(得分:2)

您可以使用InternalsVisibleTo将构造函数设置为内部并将内部公开给测试:

[assembly: InternalsVisibleTo("YourNamespace.YourTests")]

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