应该如何使 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")]
所以我有一个 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")]