How to unit test client network code?
I'm working on a piece of networking code which listens to a TCP connection, parses the incoming data and raises the appropriate event. Naturally, to avoid blocking the rest of the application, the listening and parsing are performed in a background worker. When trying to unit test this code I run into the problem that, seeing as the network code has more work to do开发者_运维知识库 than the unit test, the unit test completes before the adapter has a chance to raise the event and so the test fails.
Adapter class:
public class NetworkAdapter : NetworkAdapterBase //NetworkAdapterBase is just an abstract base class with event definitions and protected Raise... methods.
{
//Fields removed for brevity.
public NetworkAdapter(TcpClient tcpClient)
{
_tcpConnection = tcpClient;
//Hook up event handlers for background worker.
NetworkWorker.DoWork += NetworkWorker_DoWork;
if (IsConnected)
{
//Start up background worker.
NetworkWorker.RunWorkerAsync();
}
}
private void NetworkWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (IsConnected)
{
//Listen for incoming data, parse, raise events...
}
}
}
Attempted test code:
[TestMethod]
public void _processes_network_data()
{
bool newConfigurationReceived = false;
var adapter = new NetworkAdapter(TestClient); //TestClient is just a TcpClient that is set up in a [TestInitialize] method.
adapter.ConfigurationDataReceived += (sender, config) =>
{
newConfigurationReceived = true;
};
//Send fake byte packets to TestClient.
Assert.IsTrue(newConfigurationReceived, "Results: Event not raised.");
}
How should I go about trying to test this sort of thing?
Thanks,
James
Well, first, this is not a strict "unit test"; your test depends upon layers of architecture that have side effects, in this case transmitting network packets. This is more of an integration test.
That said, your unit test could sleep for a certain number of millis, as Tony said. You could also see if you can get a handle to the background worker, and Join on it, which will cause your unit test to wait as long as it takes for the background worker to finish.
You could wait for some timeout period, then run the assertion, thusly:
//Send fake byte packets to TestClient
Thread.Sleep(TIMEOUT);
Assert.IsTrue(newConfigurationReceived, "Results: Event not raised.");
Where TIMEOUT
is the number of milliseconds you want to wait.
You could use some timeout, but as always what duration should the timeout be to be sure you're test will always pass, but still not slow down your tests too much ?
I would simply test the parsing code apart. This is probably where you're going to have the most bugs, and where you most need unit tests. And it's simple to test !
Then for code that is listening on a socket ... well you could have bugs here ... but if it simply dispatches data to a function/class I'm not sure you really need to test it. And if you want to be really thorough, how are you gonna unit test that your class behaves well if the connection is lost between the client and the server for example ?
In our unit tests, we use .NET 4's parallelization library. You can say:
Parallel.Invoke(() => Dosomething(arguments), () => DosomethingElse(arguments));
And the framework will take care of spawning these actions as different threads, executing them in a number of threads ideal to the particular processes you're working on, and then joining them so that the next instruction doesn't execute until they've all finished.
However, it looks like you may not have direct access to the thread. Instead, you want to wait until the given callback method gets called. You can use an AutoResetEvent or a ManualResetEvent to accomplish this.
See Unit testing asynchronous function
精彩评论