I decided to add unit tests to my project and continue development in a test-driven kind of way. I’m currently working on implementing unit tests for my ManageSieve client object and I’m not sure what’s the best way to test that beast.
My SieveClient
object relies on two other objects for the network communication: CocoaAsyncSocket
and my own SaslConn
object, which is my wrapper around the Cyrus SASL library to handle the authentication methods. For testing I need to replace those with mock objects. I’m going to use the OCMock framework for this. I’m not quite sure how to do this, since the SieveClient object needs to create those objects itself. Right now I overwrite the (private) setters for that object to always install my mock objects using OCMocks partialMockForObject:
method. But this feels not right to me. Any ideas how this could be solved better?
The other part I have trouble with is the socket itself. To be able to test the protocol details I’d need a way to return predefined test data from the socket. I suppose I could just use OCMock mechanisms to fake the return values from the socket. But since CocoaAsyncSocket
provides many different methods to read data from the socket I have to know exactly which are being used by the protocol object in which order. I don’t want my unit test to be that dependent on implementation details of my protocol object. So what should I do here? Implement a mock object for the socket class by hand? This seems non-trivial, so I’d probably need unit tests for that too. Is that a good idea?
I’ve read that if something is hard to test it’s probably not very well designed either. But I don’t see how I could do better, since the hard part lies in interacting with the socket which I have to do.
If you’d like to see code you can find it at Bitbucket: SieveClient.m and SieveClient.h
Edit: Dependency Injection
So I read about Dependency Injection, and I think I’m going to use this to get the AsyncSocket
and SaslConn
objects into my SieveClient
object. I’ll change my constructor to accept those objects and use them. Since the user of this class usually doesn’t care about the socket and the SASL object I’ll add a factory method (in the form of a convenience constructor) that just creates those objects and passes them to the constructor.
But this solves only the first (and easier) part of my testing problem.