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
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Graham Siener

Feature hydra: how many heads does your product have?

Graham Siener
Monday, April 29, 2013

Should a web and iOS project have one or two tracks/teams/IPMs?

I posted that question to our internal Q&A forum a few months ago.  We were kicking off a client on a large project, building multiple applications for web (incl. mobile) and iOS. We expected some disparities in functionality between the two but assumed the iOS client would be a subset of the website. The client dedicated a PM for each track and there were many shared resources across both projects (designers, UX, backend, other in-house integrations).  One interesting wrinkle that would be a driving factor: the web would be a backbone app driven by an API that another team was in the process of developing.

I was fortunate to get a variety of viewpoints from Pivots that had been through similar projects (hat tip to Onsi for his great answer that influenced this post!).  My hypothesis was that we should stick to one IPM, one standup, etc. and effectively stay as one team as long as possible.  Three months in, I wanted to share an experience report on what has worked and where we’re still improving.

Clear Roles

Having a PM for each platform meant we could effectively manage the user experience specific to the device.  Mark Pincus pushes for everyone to be the “CEO of Something” and this structure ensured that the PM was not a bottleneck as our development team ramped up to six pairs.  Having one designer meant our UI and UX felt consistent across platforms.  Do not take for granted that a user transitioning from one device to another will be disoriented by an inconsistent experience.  Be careful to watch out for communication issues between PMs, and make sure they’re both describing mutually compatible products. It’s possible to specify a feature in a way that’s subtly incompatible between web and iOS — try to catch these issues early!

Group Discussion

We started off with one IPM but this grew unwieldy (talking through, and pointing out, six pairs worth of work just takes time).  We decided to split into two IPMs but the anchor from iOS joined the Web IPM and vice versa.  We also ensured that these anchors were part of a pre-IPM process that delivered the broad themes and saved the larger IPM audience from unimportant decision points.

Own the API

As I mentioned, we started off with the expectation that we’d develop our front ends while another team worked on an API to expose their backend.  It only took a few weeks to realize this would create a lot of churn and we absorbed a layer of API that let us iterate quickly and optimize endpoints for iOS.

The web is an agile-friendly platform where rapid deployment is possible. iOS, thanks to Apple’s review process, is not. Tying minor tweaks on the web client to a (potentially non-backward compatible) full deploy of the API will almost certainly land you into trouble. You have to think carefully about backward compatibility with the iOS app; imposing a clear separation between the web client and the Rails API really helps.  Have (API-level) integration tests in both your iOS suite and your web suite that hit the API app. These integration tests represent the contract that your API agrees to satisfy.

Parallelization is Hard

Understand that parallelizability will be very difficult at the beginning of the project and at major milestones of each sub-project (iOS and web). Breaking stories into tracks of work helps to highlight this.  One consequence of two backlogs is a tight coupling of stories across two Tracker projects.  iOS features that rely on additions to the API are necessarily blocked until the API team can deliver them, and the PMs must negotiate the priority of these stories in relation to web-centric features.  We opted to separate iOS and Web work, but perhaps a more fluid team could better navigate these dependencies.

Build Product One Screen At a Time

We’re actively trying to improve the story mapping/ideation process for new features.  It’s helpful to develop high-level features for all platforms at the same time, but it’s unusual that a web-focused feature can be copied verbatim to iOS.  Similarly, who breaks the tie when the web and iOS PMs have a different viewpoint on functionality or user experience?  Having one ultimate product owner could potentially address this point, yet it’s unclear how they wouldn’t be a giant bottleneck for both teams.

Closing Thoughts

Building this much (this fast) is fun!  We stood up complete experiences across three platforms while rapidly iterating on functionality.  With a team of this scale it’s critical that you’re re-Incepting your project and including the whole team in roadmap discussions to create a shared product vision.

What did I miss?  What are some best practices you’ve discovered managing products across web and mobile?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Christian Niles

Greatly Refined Story Editing in Pivotal Tracker for iOS 1.6

Christian Niles
Wednesday, February 27, 2013

Download Pivotal Tracker for iOS v1.6Today we’ve released Pivotal Tracker for iOS 1.6 to the App Store. With this release we’ve thoroughly refined the app to make updating stories easier and more enjoyable. While there are all kinds of refinements, we think you’ll be most excited about the following:

All text can now be edited inline

A story’s name, description, tasks, and comments are all editable inline, without transitioning to a new screen and losing context. While editing, text now wraps gracefully across multiple lines as you type.

For example, previous versions of the app provided a separate screen for creating a comment. In version 1.6 this now happens inline, right where the comment will be added. This allows you to continue to scan other comments or details about the story as you write.

Managing labels is easier

Finding and adding labels to a story is much improved, especially for larger projects. The label list now filters as you type, which helps avoid duplicate or mistyped labels. Adding a new label is also much more obvious and easy.

Moving Tasks doesn’t require a separate mode anymore

All tasks can now be moved using the standard move controls, without the need to go into “move” mode. This gets rid of a needless extra taps. To remove a task, simply swipe it and tap the “Delete” button.

Moving or Deleting a story are both more accessible

We’ve gotten rid of the separate “Action” screen, and replaced it with a pair of buttons to move or delete a story. You’ll find these at the very bottom of the story editor, rather than the old icon in the title bar.

We hope you love this release! Please don’t be shy about letting us know in our community about how we can continue to make the app better.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
George Dean

Test automation for iOS and smooth jazz

George Dean
Wednesday, February 6, 2013

Interestings

Appium – test automation tool for native and hybrid mobile apps

Does anyone have any experience with it?

Appium is an open source test automation tool for native and hybrid iOS apps.

  • Use Ruby, Java, Javascript etc.. to write your tests.

  • You don't have to recompile your app or modify it in any way because Appium's automation is based on Apple's UIAutomation library

http://appium.io

Smooth Jazz Nyan Cat Formatter hits 0.1.3

http://rubygems.org/gems/smooth-jazz-nyan-cat-formatter

With some display fixes. Coming eventually, configure your own music and cat!

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

Why not to use ARC

Adam Milligan
Sunday, October 7, 2012

If you have done any development for iOS in the past few years you have at least some familiarity with ARC. The overall response to ARC since Apple released it with iOS 5 has been little short of orgasmic. You can’t swing a dead internet cat without hitting a blog post from someone explaining how ARC saved his/her dying grandmother and if you’re not using it on every project you touch then you’re helping the Commies win.

I’ve seen some projects do perfectly well with ARC, but at the same time I feel it provides its own set of challenges which we should not overlook. Here are some reasons why you might want to consider not using ARC on your next Objective C project.

ARC probably isn’t solving the problem you need it to

Memory management. Programmers whisper these words in fearful tones, or brazenly avow the impossibility of doing it correctly. ARC takes care of memory management, thus solving one of the great problems of our generation, right?

As it turns out, memory management is actually quite simple; /relationship/ management presents the challenge. Memory problems are usually a result of poor relationship management. Object A leaks because objects B and C both have an ownership relationship to A, A has an ownership relationship to C, etc.

In simple cases ARC will clean up the unused memory for you, but you’re still left with a poorly designed object graph, and all its associated problems. In more complex cases you can have strong circular references — a common result of a messy object graph — and then even ARC can’t prevent the leaks.

ARC makes easy things easier, but difficult things more difficult

Here’s a simple example of a class that has a simple relationship to another class:

Class declaration without ARC:

@interface Person : NSObject
@property (nonatomic, retain) Wallet *wallet;
@end

Class declaration with ARC:

@interface Person : NSObject
@property (strong, nonatomic) Wallet *wallet;
@end

Not much difference here. Here’s a bit of the implementation:

Initializer without ARC:

- (id)init {
    if (self = [super init];) {
        self.wallet = [[[Wallet alloc] init] autorelease];
    }
    return self;
}

- (void)dealloc {
    self.wallet = nil;
    [super dealloc];
}

Initializer with ARC:

- (id)init {
    if (self = [super init];) {
        self.wallet = [[Wallet alloc] init];
    }
    return self;
}

In this simple case ARC makes life a little easier; you don’t have to type autorelease, and you don’t have to write a dealloc method to release owned objects. With a well designed object graph, these two things are the vast majority of memory management you need to do. They’re more tedious than difficult, but ARC makes them go away. That’s pretty handy, right?

Now let’s look at an example of retrieving a value from the iOS Keychain, which is a bit more complicated:

Without ARC:

NSMutableDictionary *query = [NSMutableDictionary dictionary];
[query setObject:kSecClassGenericPassword forKey:kSecClass];
[query setObject:[NSData dataWithBytes:SOME_ID length:ID_LENGTH] forKey:kSecAttrGeneric];
[query setObject:(id)kCFBooleanTrue forKey:kSecReturnData];

NSData *result = nil;
NSString *value = nil
if (errSecSuccess == SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&result)) {
    value = [[[NSString alloc] initWithData:[result autorelease] encoding:NSUTF8StringEncoding] autorelease];
}

With ARC:

NSMutableDictionary *query = [NSMutableDictionary dictionary];
[query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[query setObject:[NSData dataWithBytes:SOME_ID length:ID_LENGTH] forKey:(__bridge id)kSecAttrGeneric];
[query setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];

CFDataRef result = NULL;
NSString *value = nil;
if (errSecSuccess == SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result)) {
    value = [[NSString alloc] initWithData:(__bridge_transfer NSData *)result encoding:NSUTF8StringEncoding];
}

ARC saves us from two autorelease calls, but at the cost of five __bridge casts and a __bridge_transfer cast. ARC won’t let you declare the result variable as an NSData, since ARC disallows casting indirect pointers entirely. Thus, when you receive the result in a CFDataRef you’re still responsible for releasing that memory. You could use CFRelease() on it, or, as shown here, __bridge_transfer the CFDataRef into an NSData * temporary that ARC cleans up at the end of the execution of the statement.

Not exactly simpler, is it?

Sometimes ARC doesn’t help at all

Do you see the memory leak in this code?

self.thing = [Thing thing];
self.thing.completeNotification = ^{
    [self spreadTheWord];
};
[self.thing startTask];

By default, Objective C blocks retain everything they refer to from the local scope, including the self pointer. In this case, self retains the Thing, the Thing retains the block, and the block retains self. This creates a circular reference and a memory leak, with or without ARC.

Here’s how you fix it without ARC:

__block ThisClass *this = self;
self.thing.completeNotification = ^{
    [this spreadTheWord];
};

and with ARC:

__weak ThisClass *this = self;
self.thing.completeNotification = ^{
    [this spreadTheWord];
};

Even with ARC you still have to think about managing memory in some cases. If you rely on ARC to handle all of your memory this type of leak is probably more likely to happen.

The ARC compiler is broken

So ARC may not be helping as much as you thought, but at least it’s not really hurting anything, right? Unfortunately, turning on ARC injects bugs into the compiler. Until LLVM 4.1 (in XCode 4.5) code containing C++ templates wouldn’t even compile with ARC enabled. While LLVM 4.1 is a significant improvement over its predecessors, now the code compiles but fails to work correctly. Here’s an example of a simple function template with a specialization:

class Thing {
public:
    template<typename T>
    void do_something(const T &);
};

template<typename T>
void Thing::do_something(const T &) {
    NSLog(@"Do something with generic type");
}

template<>
void Thing::do_something(UIView * const &) {
    NSLog(@"Do something with UIView *");
}
</typename></typename>

When invoked:

Thing thing;
thing.do_something(someViewController.view);

here is the output without ARC:

2012-10-07 20:17:57.387 Project[65248:c07] Do something with UIView *

and with ARC:

2012-10-07 20:18:54.400 Project[65984:c07] Do something with generic type

What does C++ template type resolution have to do with Objective C reference counting? Nothing, as far as I can tell. It seems the ARC compiler is working off a different branch than the non-ARC compiler; a branch with significant unrelated defects. Who knows what other problems lie in wait?

Sometimes ARC is really, really broken

If you try digging around in the more esoteric capabilities of Objective C, ARC will sometimes get confused and do the wrong thing without warning you. For instance, if you’d like to change the class of an object at runtime, for example to a proxy class, with object_setClass, ARC won’t make a peep. However, it can get confused and release the object when its class changes, leading to overrelease and EXC_BAD_ACCESS. Worst of all, since ARC is completely out of your control, there’s nothing you can do about it!

Weirdly, for something that is meant to operate 100% at compile time, this behavior changes depending on what platform you run on. It happens in some instances on the simulator for older iOS versions, in different instances on the iOS 6 simulator, and (mercifully) never on a device.

ARC doesn’t really improve performance

This isn’t really a problem with ARC, but I’ve seen a number of blog posts about how great ARC is because it improves performance. These blog posts are usually filled with breathless tales of tail call optimization and assembly language listings. Now, there’s nothing wrong with Objective C from a performance perspective, but if you want to get down to questions of how many processor cycles your code takes then Objective C is pretty slow. Removing a few processor cycles from your iOS app is not going to have a noticeable effect.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Sam Coward

Cedar target templates for Xcode 4.3.x

Sam Coward
Thursday, July 12, 2012

Announcing Cedar target templates for Xcode 4.3.x: Getting started with Cedar has never been easier!

These templates let you quickly add Cedar targets to your iOS or Mac OS X project. It’s super simple to build and install from source now:

git clone http://github.com/pivotal/cedar --recursive && cd cedar ; rake install

If you don’t like building from source, you can also install a build of the Cedar templates from the downloads page – just untar to your home directory.

Using Cedar Templates

Restart Xcode after installing, open your project and add a new target. You’ll find targets for both iOS and Mac OS X. There are 2 types of targets: Suites and Bundles. A Suite target will build your tests as a separate application which you then run to execute your tests. Bundle targets work just like OCUnit testing bundles, providing closer integration with Xcode. If you do create a bundle target, make sure to edit the scheme of your application and add the new target to the list of tests.

Every template comes with a simple example which should run out of the box to help you get running.

ARC: A word of caution

Automatic Reference Counting setting: Don’t use it for your spec targets. Your application is free to use ARC, just don’t use it for any Cedar targets. This is because there is a flaw in the compiler that ships with Xcode (see here for the details, and if you care please feel free to use this information to file a bug with apple).

Have fun speccing

We really hope these changes help you to get started writing specs and spending less time setting up Cedar. If you have any feedback, whether it’s about these templates or something else about Cedar, please feel welcome stop by the discussion group and share your thoughts.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Glenn Jahnke

iOS Acceptance and Ruby Keywords

Glenn Jahnke
Wednesday, July 11, 2012

Helps

  • acceptance testing for iOS: Frank?

We’d like to do happy path end-end testing for an iOS project. What’s the state of the art?

We saw frank: https://github.com/moredip/Frank

Interestings

  • then is a keyword in ruby

If you want your ruby to read like bash:

if true then
  puts "For Sure"
end
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Dan Podsedly

New in Pivotal Tracker for iOS: Search, My Work, New iPhone Interface

Dan Podsedly
Tuesday, November 15, 2011

Version 1.1 of the Pivotal Tracker for iOS app is now available in the iTunes App Store. This update adds the Search and My Work panels, a redesigned iPhone interface, and many overall usability improvements. All the details after the jump, below.

Search and My Work

You can now find and filter stories quickly with the new Search panel, which supports all of the Tracker search syntax that you’re used to. The new My Work panel shows you all of the stories that you’re currently working on, that are assigned to you, or that are waiting for your acceptance.

Add Stories Anywhere

Adding stories is easier now, just tap the new story button at the top right corner, regardless of where you are in the app (on the iPad or the iPhone).

Faster Access to Story Panels

Switching between panels on the iPhone is now much faster with the new tab bar at the bottom. It even works while dragging a story – tap the desired target panel with the other finger.

Similarly, the iPad version now has a row of panel buttons at the top right, tap them to reveal a given panel quickly or just swipe the panels side to side as before.

Story States on iPhone

Story states are now displayed for in-progress stories on the iPhone, and easily changed by swiping a story to the left.

Other Improvements

Drag and drop and scrolling has been significantly improved, particularly on older iPhone and iPod Touch models. We’ve also updated the app for iOS 5, and added many detailed interface tweaks to make stories easier to find and work with.

Feedback

We’re committed to bringing the best of Pivotal Tracker to the your iPhone and iPad, while taking full advantage of all the user experience and portability features of the iOS platform. Many more improvements are on the way, but we’d love to know what you think so far! Give us a shout at tracker@pivotallabs.com.

By the way, we’re thrilled to see all the positive reviews in iTunes. Thanks everyone, and keep them coming!

  • 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
Dan Podsedly

Pivotal Tracker for iOS update now in the App Store

Dan Podsedly
Monday, July 11, 2011

An update of Pivotal Tracker for iOS (released last week )is now available in the iTunes App Store. It includes a number of stability fixes, including better handling of local data store corruption, and the app should no longer crash when opening a project with no stories in the icebox.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (781)
  • rails (113)
  • testing (88)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (55)
  • techtalk (44)
  • rspec (38)
  • ironblogger (32)
  • productivity (30)
  • activerecord (29)
  • gogaruco (29)
  • git (28)
  • nyc (27)
  • rubymine (26)
  • bloggerdome (23)
  • mobile (22)
  • process (21)
  • pivotal tracker (21)
  • cucumber (20)
  • design (19)
  • jasmine (19)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • tracker ecosystem (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • css (13)
  • tdd (13)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to ios Feed
  1. 1
  2. 2
  3. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • 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 >