【问题标题】:Has anyone successfully mocked the Socket class in .NET?有没有人成功地模拟过 .NET 中的 Socket 类?
【发布时间】:2010-11-18 19:31:11
【问题描述】:

我正在尝试在 C# 中模拟 System.net.Sockets.Socket 类 - 我尝试使用 NUnit 模拟,但它无法模拟具体类。我也尝试过使用 Rhino Mocks,但它似乎使用了该类的真实版本,因为它在调用 Send(byte[]) 时抛出了 SocketException。是否有人使用任何模拟框架成功创建并使用了 Socket 模拟?

【问题讨论】:

  • “你称它为 Socket 吗?”数数? =]

标签: c# nunit mocking rhino-mocks nunit-mocks


【解决方案1】:

每当我在 Moq 中遇到这类问题时,我最终都会创建一个接口来抽象出我无法模拟的东西。

因此,在您的实例中,您可能有一个实现 Send 方法的 ISocket 接口。然后让你的模拟框架模拟它。

在你的实际代码中,你会有一个这样的类

public class MySocket : ISocket
{
  System.Net.Sockets.Socket _socket;

  public void MySocket(System.Net.Sockets.Socket theSocket)
  {
    _socket = theSocket;
  }

  public virtual void Send(byte[] stuffToSend)
  {
    _socket.Send(stuffToSend);
  }

}

不确定这是否满足您的需求,但这是一种选择。

【讨论】:

  • 您也应该考虑将 send 方法设为虚拟 :)
  • 好电话。编辑使发送方法虚拟。
  • 但不要止步于此。与其只是回应外部 API,不如编写一个接口来描述您的代码想要从通信层获得什么。这可能意味着它接受更多结构化的输入,而不是字节。或者别的什么,这取决于上下文。
  • 但是,如果你让这个接口变得更复杂,你将需要对它进行单元测试,这意味着需要再次模拟内部组件......有点达不到目的。
  • 我编写了一些代码来自动生成具有此类接口和代理的程序集(用于密封/静态方法和类型)。请参阅jolt.codeplex.com/… 了解更多信息。
【解决方案2】:

上面的类只模拟你的发送方法。这实际上模拟了一个 Socket。它继承了所有的 Socket,然后实现了 ISocket 接口。 ISocket 需要实现你需要模拟的任何 Socket 方法或属性的签名

//internal because only used for test code
internal class SocketWrapper : Socket, ISocket
{
    /// <summary>
    /// Web Socket
    /// </summary>
    public SocketWrapper():base(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
    {
    }

    //use all features of base Socket
}

界面看起来像这样,有两个方法被拒绝:

public interface ISocket
{
    void Connect(IPAddress address, int port);
    int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode);
}

使用它们的类有 2 个构造函数。一个注入一个 ISocket 进行测试,然后一个注入自己的 Socket 供应用程序使用。

public class HTTPRequestFactory3
{
internal ISocket _socket;


    /// <summary>
    /// Creates a socket and sends/receives information.  Used for mocking to inject ISocket
    /// </summary>
    internal HTTPRequestFactory3(ISocket TheSocket)
    {
     _socket = TheSocket as ISocket;
     this.Setup();
    }

    /// <summary>
    /// Self Injects a new Socket.
    /// </summary>
    public  HTTPRequestFactory3()
    {
        SocketWrapper theSocket = new SocketWrapper();
        _socket = theSocket as ISocket;
        this.Setup();
    }
}

然后您的测试可以创建一个 ISocket,设置期望并验证它们是否运行上述类将用于真实套接字的所有相同代码。此测试验证该部分代码。

   [Test]
   public void NewSocketFactoryCreatesSocketDefaultConstructor()
        {
            webRequestFactory = new HTTPRequestFactory3();
            Assert.NotNull(webRequestFactory._socket);
            Socket testSocket = webRequestFactory._socket as Socket;
            Assert.IsInstanceOf<Socket>(testSocket);
        }

【讨论】:

    【解决方案3】:

    调用 Send 方法时收到 SocketException 的原因是因为 Send 不是可覆盖的方法。为了使 RhinoMocks 能够模拟属性或方法的行为,它必须要么定义在一个接口中(然后我们创建我们的模拟对象)要么是可覆盖的。

    您唯一的解决方案是创建一个可模拟的包装类(如 thinkzig 建议的那样)。

    【讨论】:

      【解决方案4】:

      我使用 thinkzig 建议的方法(Socket 的适配器类)创建了一个示例控制台应用程序。它使用 RhinoMocks 和 NUnit。你可以在这里下载:How to mock System.Net.Sockets.Socket

      【讨论】:

        【解决方案5】:

        您最好创建一个接口并在您的测试中模拟它,并在您的代码中实现一个包装类,如 thinkzig 所说,将所有方法调用转发到 .NET 套接字。看看这个链接,同样的问题:How do you mock out the file system in C# for unit testing?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-11
          • 1970-01-01
          • 2020-11-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多