9
Unit Testing WebView Objects in Cocoa
Posted at 1:03 PM by Mitchell HashimotoAnother subject which revealed no results in Google for me was how I was supposed to possibly test my models which interfaced with the web. My models use a WebView object to talk with various servers (for my UW auto registration program). Like I said in a previous post, I have implemented quite a bit of unit testing to make sure that my code continues to run smoothly. However recently when I was writing some more testing code, I realized the most important thing of all was not being unit tested: the MyUW interfacing.
Let me give a bit of a run through of what the process for using a WebView is, at least with my objects. I set my model to be a WebView’s delegate, make a request, and wait for the WebView to kick back a webView:didFinishLoadingForFrame: method. What this means is that all my web requests are asynchronous, and do not freeze up the main thread or GUI thread, which is why my program is able to run so smoothly. But this blessing is also a curse. Because the requests to my model return immediately (and a delegate call is made later to report the completion of a request), my tests also complete immediately.
For those not familiar with unit testing, let me show you a method from one of my unit tests which checks the MyUW authenticator:
- (void) testLoginDoesWorkWhenShouldWork { expectedResult = YES; [auth authenticateWithId:GOODUSER usingPassword:GOODPASS]; // OH NO! How do I test the result?! }
As you can see, the test ends… without being able to assert the result I needed to get. My first thought was to use a separate thread to run the authenticator class… which I tried. And this didn’t work. I was stumped! A bit more debugging revealed that the WebView was not even loading the URL!!! WHY?!! (These capitalizations do not even begin to correctly represent the frustration I was experiencing at this point.)
Now, let me tell you a strange fact about myself: When I have a problem, I have a very convenient way of solving it in my sleep. So when I woke up this morning, I was more or less not surprised that the solution was so clear in my head: Because the WebView is event-driven, I need to initialized a Run Loop, or else the events won’t be processed! And because this code is running within a unit test thread, and not within an NSApplication, I didn’t have the luxury of a run loop being made for me (NSApplication creates a run loop for you).
So here is the final code (more or less, I removed some method calls and copied them into this code to make it easier to read) which successfully tests the code:
- (void) testLoginDoesWorkWhenShouldWork { // 1.) Run the authentication with the expected result being a success expectedResult = YES; [auth authenticateWithId:GOODUSER usingPassword:GOODPASS]; // 2.) Initialize and loop an NSRunLoop UNTIL WE GET THE RESULT // The gotResult flag is modified in the delegate method below... gotResult = NO; BOOL isRunning; do { NSDate* next = [NSDate dateWithTimeIntervalSinceNow:1.0]; isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:next]; } while (isRunning && !gotResult); } // This method is called by the MyUW authenticator object automatically // when the authentication is complete. Result is a boolean which is... obviously... // the result. - (void) didFinishAuthenticating:(BOOL)result { STAssertEquals(expectedResult, result, @"Authentication did not get the expected result."); gotResult = YES; }
I hope this helps relieve some headache for people in the future.

Testing Asynchronous Events In Cocoa Unit Tests Apr 13, 2008 at 8:14 pm
[...] I was scratching my head trying to figure out how to unit test NSURLConnection when the Google God gave me this explanation from Mitchell Hashimoto. [...]