Thursday, March 6, 2014

A quick look at setting up RoboGuice for dependency injection in Android

Back in the day, I could do my dependency injection the old fashioned way — through the constructor. But Robolectric versions above 2.0 put an end to all that. So I created a test project to prove the merits of dependency injection using RoboGuice and Robolectric 2.2. 

Versions of Robolectric before 2.0 made it easy, albeit a little ugly, to inject dependencies into activities. There were several ways to do it and the method I preferred didn’t seem inconvenient enough to go through the rigamarole of learning to use a dependency injection framework. I admit, I may have been wrong about that one. Although one could use a test-only setter, I preferred using a constructor to pass in what I needed, which allowed me to get rid of test-only setters in lieu of test-only constructors. 

This is a pattern I used most often in making my Android demo app Friday Night Lights, of which I test-drove a large portion using Robolectric 1.2. The code below shows the basic premise of passing in dependencies through the constructor. In this case I need to mock the AppStateUtil in my test class.


public class WelcomeActivity extends Activity{
     
     private AppStateUtil mAppStateUtil; 
	
	public WelcomeActivity(AppStateUtil appStateUtil) {
		mAppStateUtil = appStateUtil; 
	}
	
	public WelcomePlayerActivity(){
		this(new AppStateUtil()); 
	}
    
    @Override
    protected void onCreate(Bundle savedInstanceState){ 
        super.onCreate(savedInstanceState);
        appStateUtil.doSomethingForTheActivity(); 
    }
}

I couldn’t get this pattern to work using Robolectric 2.2. It uses another way to create an activity and call the activity’s lifecycle methods. In the older version of Robolectric it was nothing to pass needed mocks to the activity’s constructor and then call the lifecycle methods on the activity. Those tests looked like this: 

@Mock
private AppStateUtil mockAppStateUtil; 
private WelcomeActivity mWelcomeActivity; 

@Before
public void setUp(){
    MockitoAnnotations.initMocks(this); 
    mWelcomeActivity = new WelcomeActivity(mockAppStateUtil); 
    Robolectric.shadowOf(mActivity).create(); 
}

@Test
public void itCallsSomethingOnTheMock(){
    Mockito.verify(mockAppStateUtil).doSomethingForTheActivity(); 
}

As long as the activity called the mock at some point during its opening lifecycle, then this test would be satisfied. Try to run the same test with Robolectric 2.2, you’ll find that there is no call Robolectric.shadowOf(mActivity).create(). Instead there is another way to get an activity and that is though an API that builds one. See the following for how Robolectric 2.2 does it: 


mActivity = Roblectric.buildActivity(WelcomeActivity.class).create().start().resume().get(). 

This method essentially creates a new activity and then calls any of the lifecycle methods we wish. In this case we’re calling onCreate(), onStart() and onResume(), before we return the instance of the created class with get(); 

Notice there is no constructor and therefore no trivial way to inject a dependency as before. We could rectify the situation by calling a setter on an instance variable and setting it with a mock before we need it. This approach requires creating methods that may only be used for testing and if we wanted to call that mock in the onCreate() method, I am not aware of a way to make that happen. We would need to set the instance with a mocked object between the activity being constructed (without passing it into the constructor) and the call to onCreate(). 

The answer for this is a dependency injection framework like RoboGuice. This takes away the need to pass anything to the constructor of an activity. That is because with RoboGuice the new object gets created at the moment that it’s needed. 

All we have to do is annotate the object we’re looking to inject with the @Inject annotation. So then the Activity looks like the following:


public class WelcomeActivity extends RoboActivity{

    @Inject AppStateUtil appStateUtil;

    @Override
    protected void onCreate(Bundle savedInstanceState){ 
        super.onCreate(savedInstanceState);
        appStateUtil.doSomethingForTheActivity();
    }
}

Much prettier. But there is a cost to the cleanliness of this code. That is the overhead of setting up and maintaining a dependency injection framework. Setting up RoboGuice requires some extra code in both the main project and the test project. Most of the setup deals with telling our framework how to create the object we would like to inject. 

In the example github project, we’re going to have to indicate to the Injector exactly how we new up an instance of AppStateUtil. RoboGuice does this through providers. We tell it to look for a class that implements Provider<T>. This class has the instructions of how to create an object that we need to inject. The relationship between an injected class and its specific provider is set up in another class that implements AbstractModule. This class holds the rolodex of marriage certificates between an injected class and its provider. 

As we’ve gone over, the provider knows how to create and instance of an object. In order to let the injector know how to create the AppStateUtil, we can create a provider of that type. Implementing the interface Provider<AppStateUtil> forces us to override a get() method which returns an instance of AppStateUtil. 

The provider class:


public class AppStateProvider implements Provider<AppStateUtil> {

	@Override
	public AppStateUtil get() {
		return new AppStateUtil();
	}
}

Then we bind the class type to the instructions of how to create that class. The following is the AbstractModule class that holds the instructions for how to bind these objects.


public class WelcomeGuiceModule extends AbstractModule {

	@Override
	protected void configure() {
	    bind(AppStateUtil.class)
               .toProvider(AppStateProvider.class);
	}
}

AbstractModule forces us to override configure() and that is where we are able to establish the relationship between a class an its provider. 

So now, at runtime, when the OS discovers the @Inject tag, it looks for a class that implements AbstractModule. That class says “when you want to create an instance of AppStateUtil, ask the AppStateProvider for it.” The AppStateProvider says “if you’re looking for a new AppStateUtil object, here it is… new AppStateUtil().” 

But that’s not all. We have to have a way of making sure that same helpful knowledge is available to us in the test project so that we can use our dependency injection in the test cases as well. After all, that’s really the point isn’t it? We want to inject mock objects in lieu of concrete ones. 

We’ll need another class that extends AbstractModule for our test project. This module will use the module that we defined in the main project for its binding definitions, but also it defines a method for binding an injected class to a mock object. 

Taking an example from the project, let’s take a look at the WelcomeActivityTests class, which should shed light on how we’re going to inject a mock into our class under test.

@RunWith(RobolectricTestRunner.class)
public class WelcomeFragmentTests {

	@Mock
	private AppStateUtil mockAppStateUtil;
     
        @Inject
	private WelcomeActivity mWelcomeActivity;

	@Before
	public void setUp() {
		MockitoAnnotations.initMocks(this);
		TestGuiceModule module = new TestGuiceModule();
		module.addBinding(AppStateUtil.class,
                    mockAppStateUtil);
	        TestGuiceModule.setUp(this, module);
		mActivity = Robolectric.buildActivity(
                     WelcomeActivity.class).create().start()
                    .resume().get();
	}

	@Test
	public void itCallsAppStateProvider() {
		verify(mockAppStateUtil)
                     .doSomethingForTheActivity(); 
	}
}

The setUp() tells the story. After initializing our mock object we create an instance of our AbstractModule, in this case TestGuiceModule, and then add the binding that relates the mockAppStateUtil to the AppStateUtil.class. We’ll go into more detail about this binding in a second. Next we call the static setUp() method on TestGuiceModule. As we’ll see later, this is the method that makes it so the test class knows about the bindings that are defined in the application under test.

You might also notice we’re injecting the class we’re trying to test. This works because in this situation this class is a dependency of the test class. 

To see how all this fits together, let’s take a look at the setUp() method of the TestGuiceModule. 

public static void setUp(Object testObject, TestGuiceModule module) {
    DefaultRoboModule defaultModule = RoboGuice
        .newDefaultRoboModule(Robolectric.application);
    Module welcomeModule = Modules.override(defaultModule)
        .with(new WelcomeGuiceModule());
    Module testModule = Modules.override(welcomeModule)
        .with(module);
    RoboGuice.setBaseApplicationInjector(
        Robolectric.application, RoboGuice.DEFAULT_STAGE,
        testModule);
    RoboInjector injector = RoboGuice.getInjector(
        Robolectric.application);
    injector.injectMembers(testObject);
}

Since we didn’t need to inject the WelcomeActivity object in the application we define a new binding with a provider that is defined in the test project. Assuming we have defined a HashMap named bindings, we can iterate through the mapping to bind classes to instances of a mocked object. This is what we did earlier when we called module.addBinding(AppStateUtil.class, mockAppStateUtil). 

Finally, we should create a TestGuiceModule.tearDown() method that that resets the base application injector to using the default module. This is done to keep a custom injector from negatively affecting other tests.  


public static void tearDown() {
    RoboGuice.util.reset();
    Application app = Robolectric.application;
    DefaultRoboModule defaultModule = RoboGuice.newDefaultRoboModule(app);
    RoboGuice.setBaseApplicationInjector(app, RoboGuice.DEFAULT_STAGE,
		defaultModule);

}

At last our dependency injection framework for custom objects is complete. Peace.


Saturday, February 22, 2014

Swapping Selenium::WebDriver for Appium::Driver in Rufus

In the last post I described a method for speeding up the Rufus page object gem by parsing page source data rather than making extra calls to the Selenium::WebDriver. While those changes did speed things up, it just didn’t seem fast enough. Just a few months ago I had seen Frank run through cucumber tests with blazing speed. So I posed the following question to the google group for Appium and was happy to get a quick reply from some of its contributors.


I set up a basic test project that used Appium to perform a simple automated test on the iOS simulator. It took 38 seconds to enter text into a textfield and press a button then verify some text showed up. Basically, to get the test project to run faster the Appium group had update me to Mavericks, as well as to the latest Appium revision and to Xcode 5’s latest command line tools. All good stuff I needed to do anyway, especially the Mavericks upgrade. All this allowed me to remove the  --native-instruments-lib option that I was passing in when starting the Appium server via Node. So now the test that took 38 seconds to run was down to about 12 seconds. 

But I had to get the same performance benefits in Rufus. The key difference between the test project and Rufus was the driver.  Rufus used the lower-level Selenium::WebDriver while the test project used the Appium::Driver. Even after performing the upgrades, the Rufus automated test suite did not run without passing in the --native-instruments-lib option. So those tests still were slow. Why is that option so bad? Apple injects a one second delay before any call using instruments gets executed. For Rufus that helped contribute to a test suite of 31 scenarios taking about 24 minutes to complete. 

The Appium::Driver, which is a wrapper for Selenium::WebDriver, has found a way around this delay. Not using the --native-instruments-lib option squashes the one second delay. Tests run much faster. The Rufus suite of automated tests now executes all 31 scenarios in a little over seven minutes. 

Swapping Selenium::WebDriver for Appium::Driver was no sweat. Rufus is designed to hide the complexity of backend driver implementations by having those implementations use the methods exposed by Rufus::Driver. Selenium::WebDriver and Appium::Driver were so similar that, even at a layer beneath Rufus::Driver, calls that were made to one could be made to the other without changing the method signature.

For instance

selenium.find_element(:name, 'someElement')

worked just like

appium.find_element(:name, 'someElement')


If the config.yml is set to run automated tests on a simulator (i.e. use_physical: false), then the Rufus::DriverFactory will produce an instance of IOS_Simulator. So when we call the method find(:name => ‘someElement’) on this instance of IOS_Simulator, the locator (:name -> ’someElement’) eventually gets parsed and fed into a method which calls selenium.find_element(:name, ‘someElement’). See the implementation of that method below. It is a base method that all Appium/Selenium drivers would use whether running on the simulator or a physical device. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def find(locator)
  how = locator.keys[0].to_sym
  what = locator[how]
  if how.to_s.eql?('label')
    locator = generate_name(locator)
    find(locator)
  else
    begin
      selenium.find_element(how, what)
    rescue Selenium::WebDriver::Error::NoSuchElementError
      return nil
    end
  end
end

In this case notice on line nine the method eventually calls selenium.find_element(:name, ‘someElement’). Now lets examine ‘selenium.’ It is a method that previously returned an instance of Selenium::WebDriver, but now it returns an instance of Appium::Driver.  The best part of swapping out one driver for another was that most of the work was done in one method.

It was:

def selenium
  @selenium_driver ||= Selenium::WebDriver.for(:remote, :desired_capabilities => capabilities, :url => 'http://127.0.0.1:4723/wd/hub')
end

But now it’s:
def selenium
  @selenium_driver ||= Selenium::WebDriver.for(:remote, :desired_capabilities => capabilities, :url => 'http://127.0.0.1:4723/wd/hub')
end

The new selenium method does do a few extra things to get the driver started and we’ll talk about the constructor parameters in a sec, but for the most part this was simply switching one object with another. 

The biggest hurdle so far has been in methods where I asked Selenium::WebDriver to get the class type of an element. Before the upgrade I would just call element.tag_name, but now that call throws an error saying that the element does not have an attribute ‘type.’ This Appium::Driver uses a Selenium::WebDriver greater than 2.39.0. This updated version of Selenium::WebDriver doesn’t appear to like using :tag_name as a way to get the class of an element. 

This is kinda crazy to me because if you parse the page source data, each element does have an attribute called “type” which has a value for each element. If you had a capable parser, you could get this information. Luckily, Rufus has this capability. We can ask an element for it’s class using a locator like :name => ‘someElement’ and under the covers it creates an instance of Rufus::Parser, passing in the page source data. The parser can use the page source to find an element based on the locator. In this case it would return all the key-value pairs associated with an element defined by the name ‘someElement’ then it checks for the value of the ‘type’ key. Check out how we find the class of an element then and now.

Then:

def class(locator)
  find(locator).tag_name
end

Now:

def class(locator)
  view_data = Rufus::Parser.new(page_source).find_view(locator)
  view_data["type"]
end

One of the welcome changes between the two drivers was the simplified capabilities definition for the Appium::Driver. Now we can get by with just using a hash that defines the device and the app path whereas the more low-level Selenium::WebDriver needed more.

Old way:

def capabilities 
  {
    'browserName' => iOS,
    'platform' => Mac,
    'version' => 7.0.3,
    'app' => '/path/to/App.app',
    'device' => "iPhoneSimulator"
  }
end

driver = Selenium::WebDriver.for(:remote, :desired_capabilities => capabilities, :url => 'http://127.0.0.1:4723/wd/hub')

New way:


def app
  {device: 'iPad Simulator', app_path: '/path/to/App.app'}
end

appium = Appium::Driver.new(app)

That's it for now ... peace.

Saturday, January 18, 2014

Optimizing the Rufus Page Object Gem

One of the issues my team has been encountering writing automated tests on iOS 7 with Rufus is the time it takes for Selenium::WebDriver to get a response back from the Appium server when running automated tests using cucumber. Part of the slowness could be that we're starting the Appium server using the slower, but more consistent native instruments library option.

It seemed that the selenium.find_element call took awhile to return a result, and that the selenium.page_source call returned a result more quickly. The first step in optimizing rufus to run faster was to parse the result of the page_source call for useful information. This wouldn't work in every case, but if we needed to ask selenium to find an element only to grab one of its read-only properties (such as whether or not the element was visible), it might be faster to just ask selenium to dump out the page data and we'll take it from there. 

There are several cases where we can get valuable information about the state of a screen element simply by looking at the page source data. We tabbed "read-only" data as whether or not an element is enabled? or displayed?, whether it exists? or if it had text and, if so, what was its value. We could also use the page source to determine the elements that are in a tableview and whether or not those elements are sorted, but that is not implemented yet. Below is a small sample of what the selenium.page source might return:

"{\"name\":\"showAlertButton\",\"type\":\"UIAButton\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":304,\"y\":302},\"size\":{\"width\":150,\"height\":30}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[{\"name\":\"firstChild\",\"type\":\"UIAImage\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":0,\"y\":1023},\"size\":{\"width\":768,\"height\":1}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[{\"name\":\"firstNested\",\"type\":\"UIAStaticText\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":0,\"y\":1024},\"size\":{\"width\":668,\"height\":44}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[]}]},{\"name\":\"secondChild\",\"type\":\"UIAButton\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":0,\"y\":1024},\"size\":{\"width\":568,\"height\":34}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[{\"name\":\"childOfSecondChild\",\"type\":\"UIATableViewCell\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":0,\"y\":1024},\"size\":{\"width\":468,\"height\":24}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[]}]}]}"

In reality the selenium.page_source call outputs significantly more json data, but it's clear we can parse this data into a hash that would easily produce valuable information we could use.  

For instance, the name of the first element is the 'showAlertButton' and it is of type UIAButton and this button is enabled because its enabled property is set to true (\"enabled\":true). A typical automated test might be to tell if a button becomes enabled after a user enters a value into a text field. Using rufus, one might have set up a page object called SignUpPage that defines a button accessor like so (More on page object):

button(sign_up_button, :name => 'sign up')

Then through the page object pattern a we have a way of determining at a given point in time whether or not the button is enabled or disabled.

on(SignUpPage).sign_up_button_view.enabled?

Here we're calling the sign_up accessor and through the page object we automatically get a method #{element_name}_view which returns the view element in question. Then we are calling enabled? on that return value. 

Under the covers rufus is calling selenium.find_element(:name, 'sign up') to return an object of type Selenium::WebDriver::Element, which we can then inspect. Before we decided to look into optimization strategies this call would make one call to find the element and then another call to check if that particular element was enabled?

With our optimization strategy rufus no longer calls selenium.find_element in this situation. That is because the property that we're interested in can be read from the page source data. 

Now under the covers rufus does this:

json_data = selenium.page_source  #returns json
enabled?(json_data, 'sign up')  #parses json

Rufus takes the resulting json from the selenium.page_source call then parses it, looking for the element named 'sign up'. This call to find the element strips all of the unnecessary json data, leaving only the key/value pairs pertinent to the view named 'sign up'. Then it asks for the value of the "enabled" key. All this parsing seems to happen faster than  asking selenium for the element and then asking the element if it is enabled.  

I ran a short set of scenarios for the CoPilot app at Northwoods and here were the results based on this simple change.

optimized: false
4 scenarios (4 passed)
8 steps (8 passed)
9m50.903s

optimized: true
4 scenarios (4 passed)
8 steps (8 passed)
4m53.679s