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?

asked Jan 15, 2020 at 23:49

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);
 }
 }
answered Jan 16, 2020 at 1:15

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.