0

I built an async multi-client TCP server for RPC usage. It's working well but I've found it difficult to unit test certain functionality:

  1. Connect 2x clients, is client count 2
  2. Connect 1x client, disconnect client, is client count zero

I want to test that the server is robust with handling disconnects and multiple connections. The below test fails only due to scheduling.

Unit Test

        [TestMethod]
        public void Start_TwoConnections_ClientsIsTwo()
        {
            var handler = new HandlerStub();
            using (server = new APIServer(handler))
            using (var client1 = new TcpClient())
            using (var client2 = new TcpClient())
            {
                server.Start();
                client1.Connect(IPAddress.Loopback, port);
                client2.Connect(IPAddress.Loopback, port);
                // await Task.Delay(500); <-- This will fix the problem, but is surely unreliable.
                Assert.AreEqual(2, server.Clients);
            }
        }

Server Snippet

        public void Start()
        {
            // Root try-catch, for unexpected errors
            try
            {
                server = new TcpListener(IPAddress.Loopback, 8352);
                IsRunning = true;
                do // Retry loop
                {
                    // Start server errors
                    try
                    {
                        server.Start();
                        var task = Task.Run(AcceptConnections);
                    }
                    catch (SocketException ex)
                    {
                        Console.WriteLine(string.Format("Error {0}: Failed to start server.", ex.ErrorCode));
                    }
                }
                while (!server.Server.IsBound && !IsDisposed);
            }
            catch (Exception ex)
            {
                IsRunning = false;
                Console.WriteLine(string.Format("Unexpected Error: {0}", ex.ToString()));
                throw ex;
            }
        }

        private async Task AcceptConnections()
        {
            try
            {            
                // Multi-client listener loop
                do
                {
                    var connection = await AcceptConnection();
                    connections.Add(connection);
                }
                while (!IsDisposed && IsRunning);
            }
            catch (SocketException ex)
            {
                Console.WriteLine(string.Format("Error {0}: Server socket error.", ex.ErrorCode));
                CleanupConnections();
            }
        }

How can this code be refactored to improve it's test-ability?

1 Answer 1

1

By exposing TcpListener.Pending() on my APIServer, I was able to resolve this.

APIServer.cs

        TcpListener server;
        public bool Pending
        {
            get
            {
                return server.Pending();
            }
        }

Unit Test

        [TestMethod]
        public async Task Start_TwoConnections_ClientsIsTwo()
        {
            var gazeStub = new GazeServerStub();
            using (server = new APIServer(gazeStub))
            using (var client1 = new TcpClient())
            using (var client2 = new TcpClient())
            {
                server.Start();
                client1.Connect(IPAddress.Loopback, port);
                client2.Connect(IPAddress.Loopback, port);
                await WaitForNoPendingConnections(server);
                Assert.AreEqual(2, server.Clients);
            }
        }

        private async Task WaitForNoPendingConnections(APIServer server)
        {
            while (server.Pending)
            {
                await Task.Delay(50);
            }
        }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.