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:
- Connect 2x clients, is client count 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?