Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Tools
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Stephan Hagemann

Standup 11/11/2011: Some funkinesses

Stephan Hagemann
Friday, November 11, 2011

Interesting

  • rspec stub != stub!. stub! is an alias method for stub. There is however also a method stub that is an alias for double. If you try to stub a method on the test class (to stub it on the context), you should probably use the magic subject/helper/controller methods. If you don’t, using self.stub(:name => 'result') will create a double, while self.stub!(:name => 'result') will stub the method as you would expect.

  • Asynchronous file creation and downloading: if an asynchronous process writes a file using File.open and f.write, an other process checking the presence of the file to determine whether it is already available for download, will deliver the empty file, if the file has been opened, but not yet written.

    • Workarounds:
      • if you have one write to the file only: check filesize.
      • update an ActiveRecord attribute after the file writing is completed and check against that.
  • == on DelegateClass: newing up an instance delegate_x of DelegateClass from object x, x == delegate_x, while of course x.class != delegate_x.class.

Keystroke of the day

  • Rubymine KOTD: The search+replace mode you reach via Cmd+r allows you to see recent searches by hitting the down arrow. If that doesn’t work for you in Lion, hit Ctrl+h.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

Cedar Expectations

Adam Milligan
Wednesday, November 2, 2011

As I wrote here we’ve used OCHamcrest matchers for some time for writing expectations in Cedar, but have found them unsatisfying. We wanted convenient matchers, like the ones Jasmine provides for JavaScript, but for Objective C. To that end, we added Cedar-specific expectation functions and matchers that specifically solve the problems we had with OCHamcrest.

To use Cedar’s expectations you need to make a couple small changes to your spec files:

1) Cedar matchers use C++ templates. Tell the compiler to expect some C++ code by changing the file extension on your spec files from .m to .mm.

2) Cedar matchers live in a C++ namespace. At the top of your spec file, after the includes, add this line:

using namespace Cedar::Matchers;

Features

Type Deduction

Cedar matchers determine the types of the values in the expectation, so you don’t have to. Rather than this:

assertThat(someObject, equalTo(anotherObject));
assertThatUnsignedInt(someUInt, equalToUnsignedInt(anotherUInt));

you write this:

expect(someObject).to(equal(anotherObject));
expect(someUInt).to(equal(anotherUInt));

We can also mix and match object and non-object types, in cases where such comparisons make sense. For instance, rather than this:

NSNumber *aNumber = [NSNumber numberWithFloat:1.7];
assertThat(aNumber, equalTo([NSNumber numberWithFloat:1.8));
assertThatFloat([aNumber floatValue], equalToFloat(1.8));  // equivalent

you write this:

NSNumber *aNumber = [NSNumber numberWithFloat:1.7];
expect(aNumber).to(equal(1.8));

Extensibilty

Cedar’s expectations use C++ templates not just for argument types, but for matcher objects as well. This means what goes inside the to() construct (e.g. expect(foo).to(equal(bar));) can be any object that responds to the appropriate methods (more on this later). If you want to write a custom matcher for your system (e.g. expect(user).to(be_logged_in());), no need to subclass anything or link against the Cedar library.

We also separated comparators from matchers for common expectations, such as equality. This means that adding equality comparison for a custom type involves only adding a comparator function for your type, with no need to modify the equality matcher itself. For example, here is the method for comparing two NSNumber objects:

bool compare_equal(NSNumber * const actualValue, NSNumber * const expectedValue) {
    return [actualValue isEqualToNumber:expectedValue];
}

Caveats

Several versions of LLVM have had trouble with properly compiling Objective C++ code that contains blocks, and GCC has never done so correctly. Versions of LLVM before 2.0 (Xcode 3.x) will fail to compile Cedar specs with the Internal Compiler Error” message. Version 2.0 of LLVM (Xcode 4.0.x) will successfully compile Cedar specs, but incorrectly handle reference to temporaries, leading to difficult to understand error messages. Versions 2.1 (Xcode 4.1) and 3.0 (Xcode 4.2) fix this problem, and will work properly. However, most version of Xcode still default to using the GCC/LLVM hybrid, so you may need to change this setting.

Also, C++ templates deduce types at compile time, which means they use the matcher function for the declared type of parameters. For instance, if you have an equality comparator specialized for MyType, which is a subclass of NSObject, the following code will invoke the equality comparator for NSObject, not MyType:

NSObject *foo = [[MyType alloc] init]; // refers to a MyType, but is declared as NSObject *
expect(foo).to(equal(someOtherObject));

In practice this seems to cause problems less frequently than I had expected. However, keep in mind that the declared type of nearly all Objective C initializers is id (NSNumber seems to be the exception to this rule, for some reason). So, if you pass the temporary result of an initializer to a matcher, it will most likely use the specialization of that matcher for id. For instance:

expect([NSString string]).to(equal(@""));  // Compares id to NSString * (works fine)
expect([[MyType alloc] init]).to(equal(@"")); // Compares id to NSString * (might work?)

Finally, because of the way C++ template work, the order of declaration of functions does matter. For instance, the equality matcher delegates comparison to the appropriate overload of the compare_equal function; this allows easy addition of custom comparators, as mentioned above. However, at the point where the compiler finds the equality matcher, it will only match the parameter types to comparator function overloads it has already seen. This mean you need to include your custom comparators before the declaration of the equality matcher. Since Cedar helpfully includes all matchers and comparators, you can do this in one of a few ways:

  1. Import your custom comparators before Cedar’s SpecHelper.h at the top of your file. This should work fine, since your custom matchers and comparators need not included dependencies from Cedar.

  2. Cedar checks a preprocessor value named CEDAR_CUSTOM_COMPARATORS. If you set this value Cedar will treat the value as a file path and #import it before the matcher library. Cedar also have CEDAR_CUSTOM_MATCHERS and CEDAR_CUSTOM_STRINGIFIERS (for providing custom output formats for your types).

  3. For new matchers and comparators for Cocoa types (CGRect, UIView, NSIndexPath, AVPlayer, etc.), feel free to contribute back to Cedar so they get included by default.

Please send any comments, questions, suggestions, feature request, pull requests, etc. to the Cedar discussion mailing list. Unfortunately, messages to the Pivotal account on GitHub get lost in the jillions of messages and notifications we receive.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

The Trouble With Expectations in Objective C

Adam Milligan
Wednesday, November 2, 2011

At Pivotal we write a lot of tests, or specs if you prefer. We TDD nearly everything we write, in every language we write in and on every platform we write for, so we actively work to improve every aspect of our testing tools. Personally, as I’ve written tests in Objective C I’ve found that the syntax of expectations has left much to be desired.

Like many people who write Objective C, I’ve spent a fair bit of time with languages like Ruby and JavaScript. When I write specs I often yearn for the simplicity of the expectation syntax in those languages. For example, some simple expectations in JavaScript, using Jasmine:

expect(composer.name).toEqual("Ludwig");
expect(composer.symphonies.count).toEqual(9);
expect(composer.symphonies).toContain("Eroica");
expect(composer.symphonies).not.toContain("Appassionata");

In comparison, the same expectations in Objective C, using OCHamcrest:

assertThat(composer.name, equalTo(@"Ludwig"));
assertThatUnsignedInt([composer.symphonies count], equalToUnsignedInt(9u));
assertThat(composer.symphonies, hasItem(@"Eroica"));
assertThat(composer.symphonies, isNot(hasItem(@"Appassionata"));

The challenge

I want three primary attributes from matchers: readability, ease of use, and extensibility.

Readability

With the exception of the unfortunate mismatch between the negation function (isNot) and the containment matcher (hasItem), I find the OCHamcrest expectations reasonably readable. The distinction between the first equality matcher (assertThat/equalTo) and the second (assertThatUnsignedInt/equalToUnsignedInt) is a bit jarring, though.

Ease of use

That distinction is much more of a problem for usability. The problem comes from the Objective C type system’s split personality: some variables refer to Objective C objects (NSNumber *, NSString *, NSArray *, UIView *), while the others refer to built-in types and structs (int, char *, CFArray, CGRect), and never the twain shall meet. A simple equality match must use the isEqual: method for the former, but the == operator, or something more esoteric like strncmp, for the latter. Sadly, a single matcher function has no way to determine the correct equality mechanism to use. Worse, the matcher function declarations must specify the types of their parameters, so expectations for any non-pointer types must have separate declarations for each type. The number of functions for any general purpose expectation explodes:

  • equalTo
  • equalToInt
  • equalToUnsignedInt
  • equalToFloat
  • equalToDouble
  • etc.

I can’t describe how many times I’ve failed to have the type of the assertThatX function for an expectation match the equalToX function. Frustration ensues.

Extensibility

Adding new OCHamcrest matchers isn’t terribly difficult, but you have to derive your matcher object from the HCBaseMatcher class, which means any library that includes custom matchers must link against the OCHamcrest library. If your spec target links against OCHamcrest and your custom matcher library, and your custom matcher library also links against OCHamcrest, suddenly you’re in dependency management hell.

What to do about it?

We’ve used OCHamcrest on a number of projects now, but the usability issues have become an increasing burden.

Peter Kim wrote a nice matcher library named Expecta which solves the type differentiation problem using the Objective C @encode function. We’ve used this on a couple projects, and like it more than OCHamcrest. The above example would look like this in Expecta:

expect(composer.name).toEqual(@"Ludwig");
expect([composer.symphonies count]).toEqual(9);
expect(composer.symphonies).toContain(@"Eroica");
expect(composer.symphonies).Not.toContain(@"Appassionata");

At the same time, we added a set of built-in matchers to Cedar, which solve the type differentiation problem with C++ templates, and which we designed to be extremely extensible. We’re using these matchers on some of our projects as well, and we’ve found they work nicely. Here’s the above example using Cedar matchers:

expect(composer.name).to(equal(@"Ludwig"));
expect([composer.symphonies count]).to(equal(9));
expect(composer.symphonies).to(contain(@"Eroica"));
expect(composer.symphonies).to_not(contain(@"Appassionata"));

You can read more about the Cedar matches, including how to use and extend them, here.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Nathan Wilmes

Performance tuning an old Pivotal app – how I did it

Nathan Wilmes
Wednesday, September 21, 2011

Last week, I was tasked with diving into Pivotal’s allocations application to figure out why it was operating so slowly and hopefully make it a bit better. The application was written as a side project about 4 years ago, and clearly showed its age. It’s not every day I run into an application that uses RJS! Anyways, I was able to use an incremental refactoring-based approach to improve the speed by about 80% or so. Edward and Josh Knowles suggested that I write up a bit about what I saw and how I improved it, in hopes that other engineers can make use of these performance tuning and refactoring concepts on other projects.

So, without further ado…

Set up – Measurement measurement measurement

Any time I look into a performance feature, I try to focus on getting a clear piece of functionality to optimize (in this case, the main project matrix page) and measure the heck out of its performance both on production and locally. So, the first place to go was NewRelic. NewRelic told me a couple of things – first off, there were a lot of database queries hogging most of the time. Secondly, most of those database queries involved an ActiveRecord object called ‘PersonRange’.

Adding some manual benchmarking (it’s easy! Just add a ‘stamp’ function and a before/after filter to generate a report of your timestamps) told me that many of the database queries were actually happening during view processing – a big no-no. I had a direction of investigation.

So why did the view take so long?

Like a lot of Rails projects, the view on allocations relies on a series of partials to generate a large matrix. All of these partials are looped – and with the main matrix page looping over projects, each project looping over weeks, each week looping over allocation tiles, you can imagine how the numbers add up quickly.

My first line of improvement was to streamline the innermost partial as much as possible. First off, I replaced the partial with a helper method. In general, looping over a partial is slower than calling the partial as a collection, which is in turn slower than using a helper method to generate markup. The innermost partial was very simple and easily lent itself to being a helper method. For good measure, I turned the ‘loop over allocation tiles’ code snippet into a helper method as well.

When I did this, I naturally started looking at the parameters this method needed. One strangeness – a random lookup hash named ‘roles’ was passed to this method/partial. The partial then looked up an the person’s role from this hash. The lookup hash itself was generated through a DB query generated by a helper method in the next outer partial (project_week_cell), so it generated a DB query per project per week.

On a parallel note, we also needed to look up people’s locations on a week by week basis, and there were some on the fly DB calls happening in the view layer for this as well.

So where did role and location come from? Lo and behold, both of these properties were methods of a single PersonRange object.

My direction was clear. In the controller layer, I made one set of queries to figure out the relevant person range for each person for each week. This cache was used for all location and role questions from that point forward. The cache was plopped into the main ProjectAllocationMatrix object along with the other preexisting caches, of which there were many. Boom, 50% speed improvement.

More streamlining

Before I could tackle a bigger refactor, I needed to simplify and organize the code a bit. The codebase had some obvious things to organize, that wouldn’t affect the performance much but would clarify flows and responsibilities.

  • There were some helper methods that didn’t have anything to do with view logic. The easy refactor for these kinds of methods is to figure out which of their arguments is the ‘important object’, and move the method into becoming an instance method of that object.
  • I found a few repeated patterns embedded in the views – an expression of ‘billable + unbillable + overhead’ that took three separate collections and added them together in this specific order. These three collections were only used to be added together in this way. An easy refactor consolidated them into one collection method. In the process, I simplified the calculations from three separate selects to a single sort.
  • One of the caches stored a person’s last unique set of initials. This cache was then postprocessed to generate an abbreviated name for that person. It seemed more useful to make this cache store the entire abbreviated name, to reduce postprocessing.
  • Some of the caches were exposed so that other code (mostly view logic) used them directly as a hash, knowing exactly how the cache was organized. I reduced the cohesion between the caches and the view layer, adding access methods to the matrix object that held the caches.

Taking another passthrough for performance

Even after these changes, I was still seeing some database calls in the view layer. I decided to track them down and get rid of them.

  • One of them was a ‘find location by id’ lookup. As it turned out, locations were an association from PersonRange, so making the person range cache join with location in the query was a no-brainer and removed all those queries.
  • The time calculating allocations for a week was a bit long. Looking at this code, it was running a SQL query then using Ruby to first group allocations by project ID, then sort by person for each project. I couldn’t imagine that doing an individual sort for each project was terribly efficient. I decided to do the sort first, then select the projects. This shaved a few milliseconds – no big deal.
  • Some of the matrix caches weren’t all that useful any more. There was a ‘locations by people’ cache that was used a bit. The person ranges cache could do everything this cache cared about and more – it also knew if a person’s location changed from week to week! I killed the other cache and converted to using the better information.
  • The next closest partial was ‘project_week_cell’, which was surrounded by a loop to render individual copies of this partial. I switched it to a collection partial.
  • I also found out about a code branch that stored abbreviated names on a person by person basis in the database rather than calculating it for each matrix. I incorporated it and removed another cache calculation from the matrix.
  • Around this time, I realized I could tune the ‘resource_target’ cache to cache what we actually cared about, target counts. Shifting the cache shaved a bit of time off of matrix generation in general.

All of these changes chopped another 25% or so off of the load time.

Drag/drop performance

Now the basic matrix page render was in fairly decent shape, and I moved onto the other mandate – making the drag/drop operate more smoothly. The mechanism was basically that it would perform the allocation change, recalculate the matrix for the changed projects, and then render RJS that refreshed the project rows for these changed projects.

When I looked at performance, I discovered that most of the time was spent generating two copies of the allocation matrix. The first matrix was the one mentioned in the controller (just calculating for those two projects); the second matrix was a full matrix calculated to generate refreshed billable percentages.

The interesting thing was that both matrices took almost the same time as each other. Restricting projects did not matter for performance.

As a first pass, I decided that the ‘restricted projects’ matrix was useless. If we used the bigger matrix for everything, we only had to calculate everything once and we would have everything we needed. I made this conversion and I cut server time in roughly half.

We have to refresh large chunks of HTML? Really?

In order to cut down what the server needed to refresh, I knew I needed to decouple the server-side more from the client. I’m a big fan of the Backbone-y/JSON-data-streamy approach to AJAX calls. The basic idea is this – you tell the server to do some work using AJAX, and the server returns changed model data as JSON. The server does not generate HTML markup or Javascript.

Now getting to this point is a hard problem. As an intermediate step, I decided to move the Javascript aspects of the RJS up into the client and get the server render simplified into ‘these are the chunks of HTML that I am updating’. I went with a simple JSON format using element HTML IDs as the keys and the markup as the values.

Going through this exercise was interesting. I found that many of the HTML element IDs could simply go away, as the Javascript no longer needed to refer to them to find information. I did not have Javascript model objects (that seemed like biting off too much), so I went with data attributes to store basic project/person/week IDs. Still, the markup started shrinking, which was great news.

More Javascript streamlining followed. I identified some ‘ondblclick’/'onclick’ attributes in the markup and switched over to the jQuery live event methodology to manage them more simply. The draggable elements and droppable elements became full-fledged Javascript objects (intended to eventually be Backbone views), with a matrix object to manage them.

I was now ready to do another performance pass (beyond the minor improvements from having smaller markup).

So why do we need to recalculate the whole matrix, anyways?

I returned to some of the oddities of the first drag/drop pass.

First oddity – making an allocation matrix with only 2 projects of interest was just about as expensive as making an allocation matrix with all projects. I discovered that the reason was buried in the caches – there was a low level shared cache of allocation information used both to calculate project allocations and unallocated people.

Getting rid of this cache and making these two calculations retrieve just the information they needed was the way to go. When I did this, the full matrix stayed at about the same performance level it was before. The smaller matrix, however, became much faster.

This led to the question of whether I could get away with only using the smaller matrix. The answer appeared to be ‘yes’, provided I figured out how to keep the billable percentages over all projects up to date.

Talking to Edward, I realized that the billable calculations didn’t really need a server to calculate them. All of the information they needed could be retrieved from the page – hence, the billable percentages could become a client-side concept. I switched the billable headers to be rendered using Javascript rather than the server, and then things got much lighter.

So where to, now?

Every refactor leads to a new refactoring idea. Even though my week was finished, I saw plenty of other things that could be tightened up. Among my random thoughts:

  • This project is using multiple Javascript frameworks – YUI, jQuery, jQuery-ui, and prototype. Prototype/YUI are older technologies and we don’t really need to use them any more if we go through a conversion pass.
  • YUI drag/drop requires element IDs to identify elements, and jQuery uses selectors instead. I know which technology I’d rather use. Guess which one allocations currently uses, and how many HTML IDs needed to be added to the markup because of this.
  • Some of the HTML classes were extremely duplicated because they were on the wrong elements. For example, I would see a billable project row with a whole bunch of allocation cells, each with the class ‘in_billable_project’. Why not give that class to the project row as a whole?
  • I ended up with drag/drop returning chunks of HTML markup. Chunks of basic allocation data would be a lot more lightweight, and managing cells on the client side would open up some nice possibilities for visual effects.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Android Tidbits 6/8/2011: Warning! Warning!

Joe Moore
Thursday, June 9, 2011

Un-shadowed Method Warnings!

In response to yesterday’s question about Un-shadowed Method Warnings, Pivot Tyler points out that you can turn these on by calling Robolectric.logMissingInvokedShadowMethods(). Thanks!

Bad cached-robolectric-classes.jar

On two workstations every unit test was failing with the following error:

// …many levels of stack trace, finally:
Caused by: com.xtremelabs.robolectric.bytecode.IgnorableClassNotFoundException: msg because of javassist.NotFoundException: android.content.DialogInterface$OnShowListener
at com.xtremelabs.robolectric.bytecode.AndroidTranslator.onLoad(AndroidTranslator.java:80)
at javassist.Loader.findClass(Loader.java:340)

Cause: there was an old tmp/cached-robolectric-classes.jar that was causing these errors and our tests ran successfully after deleting it. That’s two answers from Pivot Tyler!

Don’t DDOS Yourself With Your Own App

The Bump Android team wrote an article about a good idea gone wrong. Moral of the story: not all devices behave the same, and this might cripple your servers rather than the devices.

Robolectric Google Group

Join it, contribute, and learn about unit testing your Android apps.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Pivotal Android Tidbits 06/07/2011

Joe Moore
Wednesday, June 8, 2011

We’re trying an experiment: since we currently have several Android projects at Pivotal Labs, and Android development and testing is hard, we are going to post to the blog the tips, tricks, gotchas, and conundrums we find.

Emulator and Orientation

Android will destroy and recreate activities upon screen orientation change. One way to prevent this is to set android:configChanges="orientation" in your AndroidManifest.xml (article here):

<activity android:name=".activities.MyActivity"
          android:configChanges="orientation"/>

This prevents android from calling onPause() => onDestroy() => onCreate() on a device, but not within the emulator; annoyingly, the Activity is still recreated within the emulator.

Un-shadowed Method Warnings?

Once again we spent about 15 minutes debugging a failing unit test only to find that there was no Robolectric implementation/shadow for one of the Android methods under test (ArrayAdapter#insert in our case). I would be nice to have a “warning mode” where Robolectric would log warning for all un-shadowed methods. I wish we knew the guys who wrote Robolectric…

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mike Gehard

Mocking Fog when using it with Carrierwave

Mike Gehard
Thursday, April 21, 2011

There is a new kid on the block when it comes to file attachments for Rails and it is called Carrierwave.

Carrierwave gives you the ability to easily store attachments on S3 using another great gem called Fog.

Uploading files to S3 is great for many reasons but it can slow down your testing environment because it takes a while to send stuff up to S3. The Carrierwave documentation tells you how to switch the storage location over to file storage during testing but that wasn’t enough for me. I wanted to use the same storage mechanism for dev, test and production so I sought out a way to do so.

I had heard about Fog’s ability to mock itself to pretend that it was interacting with S3 so I decided to see if I could get it working with Carrierwave. This allowed me to use the same storage mechanism in test mode without slowing my tests down waiting for images to go to S3.

After a bunch of tinkering and a message on the Fog mailing list(thanks for the quick response Wesley), this is what I came up with:

The key is that you have to tell the mocked Fog that an S3 bucket exists before it will let Carrierwave put an image there. I wasn’t doing this at first and Carrierwave kept showing me a 404 error from Fog.

Drop this in a file in your spec/support and/or features/support
directories and you will have your tests thinking they are sending things to S3 without actually sending them to S3.

Now I don’t have to mess around with having a bunch of test images laying around my hard drive and I can make sure I’m using the same storage mechanism across all environments without slowing my tests down.

Your mileage may vary but I’d love to hear how this works for people and if there are any limitations. I haven’t found any yet.

UPDATE:
Check out my guest post on the Engine Yard Blog for updated details:
http://www.engineyard.com/blog/2011/mocking-fog-when-using-it-with-carrierwave/

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

Colorized output for Cedar

Adam Milligan
Thursday, December 23, 2010

Thanks to Sean Moon and Sam Coward Cedar now has colorized output on the command line:

Colorized Cedar report

If you’d like to display colorized output like this you can specify the appropriate custom reporter class using the CEDAR_REPORTER_CLASS environment variable. We do this in our Rakefiles, like so:

task :specs => :build_specs do
  ENV["DYLD_FRAMEWORK_PATH"] = BUILD_DIR
  ENV["CEDAR_REPORTER_CLASS"] = "CDRColorizedReporter"
  system_or_exit(File.join(BUILD_DIR, SPECS_TARGET_NAME))
end

You can set the environment variable in whatever way works for you. You can also set it to any reporter class you choose, so customize away.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Abhijit Hiremagalur

Standup 08/04/2010: Accounting on Rails anybody?

Abhijit Hiremagalur
Wednesday, August 4, 2010

Ask for Help

  • Double-entry/ledger based accounting in Rails (i.e. Quickbooks in Rails for free)

Ideally would be tied in with user/role system where each accounting entry would be tied to a user, but also reconciled against a master account.

Recommendations? Latest and greatest?

One suggestion was to look at the code that Wesabe open sourced code when they closed their doors.

  • Error message when opening Rubymine “Invalid Git Root”

This is likely because the project included a submodule that wasn’t configured correctly, fix this in under Rubymine’s version control preferences.

Add submodule screen in Rubymine

Interesting Things

  • Test 404 handling (e.g. rescue_from ActiveRecord::RecordNotFound, :with => :render_record_not_found) with Cucumber by temporarily setting ActionController::Base.allow_rescue = true. This is usually set to false in features/support/env.rb
  • JSON.pretty_generate hates Rails 3 Hashes

 

  • Use window.postMessage to communicate between IFrames in a standard way

This should work in most modern browsers. Follow the Mozilla docs, NOT the various blog posts about this.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

iPhone UI Automation tests: a decent start

Adam Milligan
Tuesday, July 20, 2010

Apple’s inclusion of the UI Automation component in Instruments with iOS 4 is a definite step in the right direction. It’s the first reasonable way to write tests that externally exercise your actual app, rather than weirdly injecting test code into it. It’s also the only way to programmatically test lifecycle issues, such as how your app behaves when put in the background, when rotated, when the device locks, etc. Good stuff. Unfortunately, the current implementation of UI Automation also has some significant problems:

  1. There’s no way to run tests from the command line. The subtitle of the WWDC talk for UI Automation was “find bugs while you sleep;” unfortunately, you can’t find bugs while you sleep if you have to wake up to click the “Run” button.
  2. There’s no way to set up or reset state. The lack of fixtures which set up a known state at the beginning of iPhone tests has been a problem for unit testing (with OCUnit, Cedar, or what have you), particularly for apps that use CoreData. Now it’s worse than ever, because UI Automation manipulates the actual state of the app on the device, much like Selenium does in a browser. Sadly, UI Automation provides no method for reseting the device’s state, making it nigh impossible to prevent tests from affecting one another.
  3. Part of the previous problem is that UI Automation has no concept of discrete tests; it provides no form of organization for your test scripts. No test methods, no set up or tear down methods, just one big stream of consciousness line of execution. Obviously you can break this up into functions as you see fit, but why reinvent the wheel? Since the test script is JavaScript, I like the idea of using Jasmine for this.
  4. There’s no way to programmatically retrieve the results of the test run. You could debate the value of solving this issue at the moment, considering there’s no way to programmatically start the tests either. However, even if you were to write some clever AppleScript to kick off the tests automatically the only indication of the pass/fail status is in the Instruments UI, so you still have to wake up to check the results. I searched around a bit for information on deconstructing the protocol that UI Automation uses to talk to the device, but I came up empty.

I’ll definitely use UI Automation, particularly for app lifecycle testing. But, not being able to add those tests to a CI build definitely stings. I very much hope Apple keeps their momentum for automated testing and makes it more developer-friendly.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (783)
  • rails (117)
  • testing (90)
  • ruby (86)
  • ruby on rails (71)
  • jobs (62)
  • javascript (59)
  • techtalk (44)
  • ironblogger (42)
  • rspec (39)
  • bloggerdome (34)
  • productivity (34)
  • activerecord (30)
  • rubymine (30)
  • git (29)
  • gogaruco (29)
  • nyc (27)
  • design (24)
  • mobile (23)
  • pivotal tracker (22)
  • process (21)
  • cucumber (21)
  • jasmine (19)
  • ios (18)
  • tracker ecosystem (17)
  • webos (17)
  • objective-c (17)
  • fun (16)
  • android (16)
  • palm (16)
  • ci (16)
  • "soft" ware (16)
  • bdd (15)
  • tdd (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • css (14)
  • gem (13)
  • mouse-free development (12)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • api (12)
  • keyboard (11)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
Subscribe to testing Feed
  1. ←
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Tools
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >