如何通过实现接口来模拟TCPClient

本文关键字:模拟 TCPClient 接口 何通过 实现 | 更新日期: 2023-09-27 17:53:08

我刚接触计算机科学,对网络知识知之甚少。我实习的时候有个任务。它是通过实现接口来模拟TCPClient。其思想是,由于我们不需要连接到真实服务器,我们只需要一个模拟TCPClient来接收数据、保存数据并发送出去。从我的理解,它只是模拟sendmessage函数在TCPClient??

public interface ITCPClient
{
    /// <summary>
    /// Event handler when TCP client is connected 
    /// </summary>
    event ConnectionEventHandler Connected;
    /// <summary>
    /// Event handler for TCP Client on receiving data
    /// </summary>
    event DataReceivedEventHandler DataReceived;
    /// <summary>
    /// Event handler when TCP client is disconnect
    /// </summary>
    event ConnectionEventHandler Disconnected;
    /// <summary>
    /// Reports error when an error occurs
    /// </summary>
    event ErrorEventHandler OnError;
    /// <summary>
    /// Set whether to use HostByteOrder or NetworkByteorder with the socket - gateway expects network byte order
    /// </summary>
    bool ByteOrder { get; set; }
    /// <summary>
    /// Returns true if the client is running and connected to the server
    /// </summary>
    bool IsRunning { get; }
    /// <summary>
    /// Add the message to the sendqueue. The message will be processed by the ProcessMessage function waiting for the message.
    /// </summary>
    /// <param name="Message"></param>
    void SendMessage(string Message);
    /// <summary>
    /// Add the message to the sendqueue. The message willbe processed by the ProcessMessage function waiting for the message.
    /// </summary>
    /// <param name="Message"></param>
    void SendMessageHiPriority(string Message);
    /// <summary>
    /// Starts the client and spawn the thread for processing the message
    /// </summary>
    void Start();
    /// <summary>
    /// Disconnects and stops listening for messages 
    /// </summary>
    void StopProcessing();
}
public class MockTCPClient : ITCPClient
{
    #region Private Fields
    private string msg;
    #endregion Private Fields

    #region Constructor
    public MockTCPClient()
    {
        //nothing 
    }

    #endregion Constructor
    #region event
    public event ConnectionEventHandler Connected;
    public event ConnectionEventHandler Disconnected;
    public event ErrorEventHandler OnError;
    public event DataReceivedEventHandler DataReceived;
    #endregion event 
    #region property
    //question 
    public string ReceivedMessage
    {
        get
        {
            return msg;
        }
        set
        {
            msg = value;
        }
    }
    #endregion property
    //question??
    private void OnDataReceived(object sender, string e)
    {
        DataReceivedEventHandler dataReceived = DataReceived;
        if (dataReceived != null)
            dataReceived(this, e);
    }

    #region ITCPClient Members
    #region properties
    public bool ByteOrder { get; set; }

    public bool IsRunning { get; }
    #endregion properties
    public void SendMessage(string Message)
    {
        msg = Message;
    }
    public void SendMessageHiPriority(string Message)
    {    
    }
    public void Start()
    {
    }

    public void StopProcessing()
    {
    }

    #endregion ITCPClient Members
}

如何通过实现接口来模拟TCPClient

这演示了Moq如何验证你的类调用了它的一个方法。

这个测试


创建一个模拟的ITCPClient
-指定SendMessage方法是可验证的——换句话说,它的使用将被跟踪。
-验证在模拟对象上调用了SendMessage("test");

测试将失败,因为它从未调用SendMessage("test");

[TestMethod]
public void TestTcpClient()
{
    var mockedTcpClient = new Mock<ITCPClient>();
    mockedTcpClient.Setup(x => x.SendMessage(It.IsAny<string>())).Verifiable();
    //mockedTcpClient.Object.SendMessage("test");
    mockedTcpClient.Verify(x=>x.SendMessage("test"));
}

如果我们取消注释调用SendMessage("test")的行,那么测试将通过。

因此,在"实际"使用中,您将创建Mock并在测试依赖它的类时使用它来代替ITCPClient。当它完成后,你可以Verify,你的类调用SendMessage,并传递预期的值。

如果你不能使用Moq,这里有一个完全不同的方法:

创建实现ITCPClient的类,如下所示。您可以将大多数方法保留为空,只实现SendMessage以及其他一些细节:

public class TcpClientMock : ITCPClient
{
    private readonly List<string> _sentMessages;
    public TcpClientMock(List<string> sentMessages)
    {
        _sentMessages = sentMessages;
    }
    public void SendMessage(string Message)
    {
        _sentMessages.Add(Message);
    }
    //rest of non-implemented methods
}

在单元测试中,这样做:

var sentMessages = new List<string>();
var mockedTcpClient = new TcpClientMock(sentMessages);

然后创建您要测试的任何类并对其进行操作。完成后,查看sentMessages是否包含您期望的消息。


至于接收数据,我认为您需要告诉您的模拟对象引发该事件,以便您可以验证您的类在引发事件时是否按预期响应。您可以像这样为TcpClientMock添加一个方法:

public void SimulateDataReceived(object sender, DataReceivedEventHandlerArgs args)
{
    DataReceived(sender, args);
}

这样,当您设置了依赖于ITCPClient的类时,您可以在模拟上调用SimulateDataReceived,使其引发事件,这反过来将导致测试对象响应。