I'm trying to mock out the System.net.Sockets.Socket class in C# - I tried using NUnit mocks but it can't mock concrete classes. I also tried using Rhino Mocks but it seemed to use a real version of the class because it threw a SocketException when Send(byte[]) was called. Has anyone successfully created and used a Socket mock using any mocking framework?
问题:
回答1:
Whenever I run into these kinds of problems with Moq I end up creating an interface to abstract away the thing I can't mock.
So in your instance you might have an ISocket interface that implements the Send method. Then have your mocking framework mock that instead.
In your actual code, you'd have a class like this
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);
}
}
Not sure if that meets your needs, but it's an option.
回答2:
The reason you get a SocketException when you call the Send method is because Send is not an overridable method. For RhinoMocks to be able to mock the behavior of a property or method, it has to either be defined in an interface (which we then create our mock off) or is overridable.
Your only solution to this is to create a mockable wrapper class (as suggested by thinkzig).
回答3:
I've created an example console application using the method thinkzig suggests (an adapter class for Socket). It uses RhinoMocks and NUnit. You can download it here: How to mock System.Net.Sockets.Socket.
回答4:
The above class only Mocks your Send Method. This actually mocks a Socket. It Inherits all of Socket and then Implements the ISocket interface. ISocket needs to implement the signatures of any Socket methods or properties you need to mock
//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
}
The interface looks like this with two methods declined:
public interface ISocket
{
void Connect(IPAddress address, int port);
int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode);
}
The Class that uses them has 2 constructors. One injects an ISocket for testing and then one that makes it's own Socket that the application uses.
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();
}
}
Then your tests can create a ISocket, set up expectations and verify them running all the same code the above class will use with a real socket. This test validates that section code.
[Test]
public void NewSocketFactoryCreatesSocketDefaultConstructor()
{
webRequestFactory = new HTTPRequestFactory3();
Assert.NotNull(webRequestFactory._socket);
Socket testSocket = webRequestFactory._socket as Socket;
Assert.IsInstanceOf<Socket>(testSocket);
}
回答5:
You'd better to create an interface and mock it in your test, and implement a wrapper class in your code, that forward all method calls to .NET socket as thinkzig said. Look at this link, it's same issue: How do you mock out the file system in C# for unit testing?