Thursday, April 29, 2010

Moving forward with customized keyboards in iphone 3.2

I recently inherited a project that had customized iphone keyboards by adding a toolbar following the techniques in http://www.iphonedevsdk.com/forum/iphone-sdk-tutorials/7350-adding-subviews-custimize-keyboard.html.

Sadly, this code behaves badly in the new SDK 3.2, it crashes. First time because of a index out of bounds, and once that was fixed then an infinite loop on keyboardDidShow. (And yes I did catch the object name change too). Luckily, 3.2 provides a new handy method to simply add the inputAccessoryView with out all the previous fuss.

But, do you want to release just a 3.2 app? If your in my position, the business sponsors do not want to hear that their hard won customized toolbar will only be seen on the iPad for a while. They expect backwards compatibility.

I solved this dilemma by using the UIInputTextField instancesRespondTo inputAccessoryView to guard the call that sets up the keyboard event listeners required in the older solution, so that only on 3.1 devices will the keyboard intercepts be executed. Then I setup a new message type AddToolbar to be called by TextFieldDelegates for fields where the toolbars will be needed.

Note that Apple does not 'publish' the Notification that triggers shouldTextBeginEditing or textFieldDidBeginEditing so instead of just having a global delegate I instead trigger this new AddToolbar notification as part of my TextFieldDelegate response.

The new message listener again checks for inputAccessoryView support and in 3.2 code makes the call to add the new Toolbar.

Saturday, April 24, 2010

A Trick for EasyMock with Legacy Code...

... where "Legacy Code" is, to paraphrase Michael C. Feathers, any code that's been in production longer than 3 months. Or in my case a new project coded over a year ago, by consultants, on a tight schedule. Lots of classes had been developed without consideration for unit testing which means lots of inter-dependent code without any hooks for dependency injection.

For example, there is a standard Config utility class that loads a property file and then provides getter methods for the values.

  • Hurdle #1: The Utility class only expects a ServletContext as a means to getting the file name to load.

  • Hurdle #2: Various path manipulations have been hard-coded in the file loading method as well.

  • Hurdle #3: The internal properties object holding the values is private accessor, designed to be read only by client classes.

  • Hurdle #4: Set up for ease of use with static method fields, so I can't just create an instance of my own or subclass easily.



In this project's case, I agree that the access to the config needs to be read only, as only bugs and mischief would result if later generations of developers thought that public or protected access to the Properties meant the config was expected to change at run-time. I also like the static behavior as it makes for clean code with no extraneous getInstance() or setConfig() calls everywhere the class is used.

On the other hand, I am simply too lazy to enjoy having to 1) set up my local environment to exactly match server paths. 2) build and deploy my code to test every code change and 3) stop and start the server to test every property permutation.

So, the challenge with this legacy code was to figure out how to inject properties at Test time without any impact on the existing code. Thankfully in Java I have Reflection and EasyMock.

First - a snapshot of the config class:
public class AppConfig {
 private static Properties applicationProperties;
 public static void loadConfig(ServletContext context){
  ... load files into appProperites
   //don't worry about this being static on
   //a constructed object it has been working over a year now..
 }
 public static String getProperty(String propKey) {
  return appProperties.getProperty(propKey);
  }
}

However, good isolated unit tests require I bypass all dependencies on file loading etc. so the first trick is abusing reflection to give me a point to inject a config mock object. Then I can create an EasyMock wrapper on the Properties object to inject which ever specific property and value my client code will need.

// written as a junit method for simplicity here
import org.easyMock.classextension.*;

public void testSomeObject() {
 AppConfig env = new AppConfig();
 Class c = env.getClass();
 try {
  Field appProps = c.getDeclaredField("applicationProperties");
  appProps.setAccessible(true);
  Properties overRideProps = EasyMock.createMock(Properties.class);
  EasyMock.expect(overRideProperties.getProperty("testkey")).andReturn("false"); //sets up our test property
  EasyMock.replay(overRideProps); // readies mock object to act during test.
  appProps.set(env, overRideProps); //puts our mock back into AppConfig object
 } catch (SecurityException e1) {
  fail ("denied access to reset applicationProperties to public " + e.toString());
 } catch (NoSuchFieldException e2) {
  fail ("applicationProperties field name changed " + e.toString());
 } catch (IllegalAccessException e3) {
  fail ( "Unable to override properties in AppConfig " + e.toString());
 } catch (IllegalArgumentException e4) {
  fail ( "Unable to override properties in AppConfig " + e.toString());
  //Note that you might see this exception if you import wrong form of EasyMock must use classextension
}

SomeObject so = new SomeObject();
SomeState testState = new SomeState();
... setup testState ...
String testResult = so.doSomeMethod(testState);
// If the above method does not call the expected property test will fail with EasyMock exception.
// otherwise it will fail if the following assertion fails.
assertEquals( "expectedResult", testResult);
}

Finally, a few words of caution on this style of testing

This test is brittle, it has knowledge of internal code working that would be better it not have. Now neither the Properties field name in AppConfig, nor the property key String can change without breaking this code. So there is a risk that a future developer will curse my name for requiring him to fix test cases for no good reason. If this were not Stable Legacy Code I would be more concerned about this risk.

This test case doesn't guarantee the system will behave as expected once deployed. There are no protections against keys changing in the property file used to load the AppConfig causing runtime defects. So some form of integration testing is needed to ensure the overall system works as intended later.

EasyMock may seem like overkill here, as I could just as easily injected a plain old Properties object. The benefit of EasyMock is that I can see exactly where the test fails and why - wrong key is used or no check is made. For all other reasons the final assert may fail I've got to go in and spend time debugging.