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

时间:2016-06-29 21:31:46

标签: c# unit-testing mocking

我是计算机科学的新手,对网络知之甚少。 我在实习中得到了一个任务。它是通过实现接口来模拟TCPClient。 我们的想法是,由于我们不需要连接到真实服务器,我们只需要一个模拟TCPClient来接收数据,保存并发送出去。据我所知,它只是模拟TCPClient中的sendmessage函数吗?

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

}

1 个答案:

答案 0 :(得分:1)

这演示了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,导致它引发事件,从而导致测试对象响应