Ibanez1408
Ibanez1408

Reputation: 5048

Running in Test using XUnit

Trying to learn Test Driven Design for the first time. I have a server that goes like this:

public class ServerEngine
    {
        private static Socket _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        private static List<Socket> _clientSockets = new List<Socket>();
        private static byte[] _buffer = new byte[1024];

        public ServerEngine()
        {
            SetupServer();
        }

        private void SetupServer()
        {
            Console.WriteLine("Setting up the server...");
            _serverSocket.Bind(new IPEndPoint(IPAddress.Any, 100));

            // Settingup the backlog
            _serverSocket.Listen(1);

            //Listen for connections
            _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
        }

        private void AcceptCallback(IAsyncResult ar)
        {
            // Add the accepted socket to the list of sockets
            Socket socket = _serverSocket.EndAccept(ar);
            _clientSockets.Add(socket);
            Console.WriteLine("A client has connected");
            // listen for messages coming from the new accepted socket
            socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
            // Start accepting a new connection again
            _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;
            try
            {
                var received = socket.EndReceive(ar);
                var dataBuf = new byte[received];
                Array.Copy(_buffer, dataBuf, received);

                var text = Encoding.ASCII.GetString(dataBuf);
                Console.WriteLine($"Text Received: {text}");

                var response = AddToHashSet(text);
                var data = Encoding.ASCII.GetBytes(response.ToString());
                socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket);
                socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
                _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
            }
            catch (SocketException)
            {
                socket.Close();
                Console.WriteLine("A client has disconnected.");
            }
        }

        private void SendCallback(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;
            socket.EndSend(ar);
        }

        private HashSet<TidContainerModel> _tidContainerModels = new HashSet<TidContainerModel>(new TidContainerModelComparer());
        private bool AddToHashSet(string value)
        {
            var tcv = new TidContainerModel
            {
                Tid = value,
                TimeAdded = DateTime.Now,
                Type = 'R',
                Rssi = -444
            };
            return _tidContainerModels.Add(tcv);
        }
    }

This I call in the Program Main to setup the server. Then I created an xUnit test like so:

private Socket _clentSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        private ServerEngine serverEngine = new ServerEngine();

        [Fact]
        public void ConnectToServerTest()
        {
            // Arrange
            bool expected = true;

            // Act
            bool actual = LoopConnect();

            // Assert
            Assert.Equal(expected, actual);
        }

        private bool LoopConnect()
        {
            int attempts = 0;
            var connected = false;
            while (!_clentSocket.Connected)
            {
                try
                {
                    attempts++;
                    var ipAddress = new IPEndPoint(IPAddress.Parse("172.16.35.71"), 100);
                    _clentSocket.Connect(ipAddress);
                }
                catch (SocketException se)
                {
                    Console.Clear();
                    Console.WriteLine($"Failed connecting {se.Message}. Reconnecting attempt {attempts}");
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }

            connected = true;
            Console.Clear();
            Console.WriteLine("Connected to the server");
            return connected;
        }

Without using the Test this program runs fine. But when I use the test, I fail with an error of:

Server.Test.ServerEngineTest.ConnectToServerTest
   Source: ServerEngineTest.cs line 15
   Duration: 209 ms

  Message: 
System.IO.IOException : The handle is invalid.


  Stack Trace: 
__Error.WinIOError(Int32 errorCode, String maybeFullPath)
Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
Console.Clear()
ServerEngineTest.LoopConnect() line 51
ServerEngineTest.ConnectToServerTest() line 21

enter image description here

If I use a console application to connect to the server, it works perfect. I want to learn how to use the xUnit with this project.

Upvotes: 0

Views: 133

Answers (1)

Blindy
Blindy

Reputation: 67352

Look at your stack trace, it's telling you the problem:

  Stack Trace: 
__Error.WinIOError(Int32 errorCode, String maybeFullPath)
Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
Console.Clear()
ServerEngineTest.LoopConnect() line 51
ServerEngineTest.ConnectToServerTest() line 21

Basically, you're trying to clear the console, but you have no console allocated in your test. Which is something you should never do for two reasons:

  1. You should be mocking your I/O to be able to test it.
  2. You should absolutely never mix this kind of console I/O with your socket service, whatever it does. Use dependency injection to extract and contain console I/O (or even better, a proper logging interface) separate from the actual work of starting and maintaining a TCP server.

Upvotes: 2

Related Questions