OCMock Test Origami

03 Mar 2014

Audience

This article assumes the reader is familiar with testing in Xcode 5 using XCTest, BDD style Kiwi or another iOS testing framework.

What is mocking ? Paper tigers, mostly

paper tiger

When unit testing, it’s imperative to instantiate as little concrete components as possible to keep tests short, fast, and preserve unit isolation. In a modern Object Oriented system, the component under test will likely have several object dependencies. Instead of instantiating dependencies as concrete classes, we use mocks. Mocks are ‘fake’ objects with pre-defined behavior to stand-in for concrete objects during testing. The component under test does not know the difference! With mocks, a component can be tested with confidence that it behaves as designed within a larger system.

Common mock use cases

Stubbed methods

To start, here’s a simple example which explains the general stubbing grammar in OCMock :

 id jalopy = [OCMock mockForClass[Car class]];
 [[[jalopy stub] andReturn:@"75kph"] goFaster:[OCMArg any] units:@"kph"];
 
 // if returning a scalar value, andReturnValue: can be used
 

This contrived example first creates a mock jalopy from the class Car. Next, it stubs out the method goFaster: to return the NSString @”75kph” . The stubbing syntax may seem a litle weird at first, but this is the general idea :

ourMockObject stub] whatItShouldReturn ] method:

One very important note: , notice the usage of [OCMArg any] . When specifying a method which takes parameters, the mock will return the value specified by andReturn: only when the method is invoked with the provided parameters. The method [OCMArg any] tells the stub to fire for any parameter value. In the example, an invocation of

[car goFaster:84 units:@"mph"];

Would not trigger the stub because the last parameter units: does not match “kph”

Class methods

OCMock will find class methods on the mock instance as long as there is not an instance method with the same name. In the event of same named methods, employ the classMethod method:

[[[[jalopy stub] classMethod] andReturn:@"expired"] checkWarrany];

Types of mocks - niceMock, partialMock

OCmock provides several diffent types of mocks, each with their specific use case.

Any mock created in this fashion :

 id mockThing = [OCMock mockForClass[Thing class]];

Is what I call a ‘vanilla’ mock. Vanilla mocks will raise an exception if an un-stubbed method is invoked. This can get a tedious as every single method called during the mock’s lifecycle must be stubbed. (more on stubbing in the next section)

If stubbing out many methods isn’t your thing, use a ‘nice’ mock. Nice mocks are polite and do not raise an exception if an unstubbed method is invoked.

 id niceMockThing = [OCMock niceMockForClass[Thing class]];

The last type of mock is a ‘partial’ mock. When an unstubbed method is invoked, that method call is forwarded onto the actual object. That’s technically cheating with mocks, but can be useful when there are aspects of the class which don’t lend themselves well to stubbing.

Thing *someThing = [Thing alloc] init];
id aMock = [OCMockObject partialMockForObject:someThing]

Verify method was or wasn’t called

Verifying a method was or was not called is easy. This can be accomplished by expect , reject and verify methods :

 id niceMockThing = [OCMock niceMockForClass[Thing class]];
 [[niceMockThing expect] greeting:@"hello"];
 
 // verify the method was called as expected
 [niceMocking verify];
 

The verify method will throw an exception if the method was not called. If you’re using XCTest, wrap the verify call within an XCTAssertNotThrow . Reject works the same way, but will throw when the method is called on the mock. Just like when stubbing, the selector and arguments passed to verify must match those passed by the caller. Use [OCMArg any] to make things easier.

Dealing with block arguments

Block callback parameters can also be handled by OCMock. Block callbacks are common in networking code, database code, or anywhere async operations are plentiful. For this example, consider the following method :

- (void)downloadWeatherDataForZip:(NSString *)zip
              callback:(void (^)(NSDictionary *response))callback;

In this example, we have a method which downloads weather data for a given zip and delegates the downloaded dictionary into a block callback. To test, let’s pass pre-defined weather data to test callback handling. It’s also advisable to test failure scenarios; you never know what can come from the network!

// 1. stub using OCMock andDo: operator.

[[[groupModelMock stub] andDo:^(NSInvocation *invoke) {
        //2. declare a block with same signature
        void (^weatherStubResponse)(NSDictionary *dict);
        
        //3. link argument 3 with with our block callback
        [invoke getArgument:&weatherStubResponse atIndex:3];
        
        //4. invoke block with pre-defined input
        NSDictionary *testResponse = @{@"high": 43 , @"low": 12};
        weatherStubResponse(groupMemberMock);
        
    }]downloadWeatherDataForZip@"80304" callback:[OCMArg any] ];

The general idea here is reletively simple, even though it’s implementation requires some explanation:

  1. This mock uses the ‘andDo’ method which accepts an NSInvocation argument. An NSInvocation object represents an ‘objectivetified’ representation of a method invocation. Through this NSinvocation object, it is possible to intercept the block parameter passed to our function.
  2. Declare a block parameter with the same method signature as the one in our test method.
  3. NSInvocation instance method ‘getArgument:atIndex:’ assigns the block parameter passed to the original function to our locally declared block variable. Note : in Objective-C, the first two parameters passed to any function are ‘self’ and ‘_cmd.’ This is a little feature of the runtime and something to consider when grabbing parameters by index from an NSInvocation.
  4. Finally, pass the callback a pre-defined dictionary.


In closing

paper tiger

Hopefully this article and examples have clarified some of the most commons uses of OCMock. The OCMock site: http://ocmock.org/features/ is a the best reference for everything in the OCMock world.

Mocking can be tedious but is necessary to fully test a modern, OO system. If a dependency graph is difficult to test with mocks, this is an indication the design may need to be re-considered.

Another great OCMock article

Making fun of things with OCMock


Slim XCTest Targets

24 Feb 2014

What is XCTest ?

XCTest has made testing on iOS dead simple easy; click a few buttons and a shiny new XCTest target is added to the project. There are still developers out there who don’t test, and the difficultly of adding test machinary to a project is no longer an excuse.

If you’re new to XCTest and want to give it a try, I recommend the 2013 WWDC video “Testing in Xcode 5.” Thirty minutes and you’ll be up to speed.

Adding XCTest to an existing project

The procedure to add XCTest to a new project is exactly the same as adding to an existing except for one important detail. To keep the XCTest target slim, the setting Symbols hidden by default must be switched to NO

symbols hidden changed to NO

Don’t add app sources to the test target!

The XCTest framework is injected into the app target at runtime and now has access to the symbols contained within. It’s not necessary to re-add these source files to the XCTest Target.

Remember, the XCTest target compile sources only requires test files. That’s it!

xctest target compile sources


Chief Technical Gardener

01 Feb 2014

‘Technical co-founder’ may sound like a dream to the common coder. However busting out code is only one responsibility for a new technical co-founder. While technical competence is important, there are other more ‘human’ facets to the role:

  • Responsibility for initial technical decisions and execution of first prototype
  • Plant the seeds and tend the fledgling development process and procedures. ie - tooling, deployment, engineering culture
  • Be a solid rock the CEO and business can trust to make sound technical decisions based on business needs
  • The human face of the engineering team! :)

butterfly and code

Hacker chops wrapped in sound business judgement

What differentiates quality technical founders isn’t technical wizardry, but judgement - the ability to correctly and effectively in weigh technical tradeoffs against business objetives. These decisions are tough, and the company may have to live with the outcome of these decisions for a long time. The measure of a good decision is the abilty to forsee the ramifications when circumstances outgrow the solution. Good judgement differentiates hype from fact and doesn’t make technology decisions based on shiny.

Tending a tidy garden for others

When coding in a rush, the project can get a little out of whack - bad or no convention, poorly named files, blasphemous hacks; it happens. Especially if you are a single founder or with a small team. Tribal knowledge is a killer It’s a real time waster to bring another dev up on an unconventional design all because the technical founder was too lazy to follow a convention. Keep things tidy, write conceptual documentation, write tests!. Prepare the garden as if others were coming to tend.

Maintaining the daily rhythm, sans drum circle

As the chief uber hacker, the technical founder sets the cadence of the fledging development team. Daily standups serve as a fantastic foundation to the days activities. Critical bugs, dev tasks and other miscellaneous dev detris are prioritized and tackled. The team must make progress every day to be successful; daily success requires short, daily planning. Later when the company is successful, a scrum master and PM may fullfill this duty.

Technical debt can be tricky to keep under control during the early times. Code reviews via Github or BitBucket are an amazing way to keep the team aware of changes and address bad designs before they make it into the product.

Regular internal demos allow the team to really shine and show off work accomplished during the last iteration. It’s important to keep motivation high by reflecting on progress and congratulating those who kicked ass in the last sprint.

Good leaders lead, great leaders delegate

One of the best pieces of advice for new technical founders is the 80% rule. If a junior developer or outsourced help is 80% effective at a task, delegate it. Review their work before the code makes it into the project and provide feedback. Trust in others on your team will allow them to become more involved in the company and take personal ownership of features. Each team member will eventually tend their own plot of land inside the codebase.

Don’t Over water -

No developer is an island; as much as one would like to paddle out in the ocean to a peaceful place devoid of people to get some coding done, the technical founder must remain plugged into the daily goings on of the business as the human face of the development team. As the key interchange in which business needs are turned into business value through technical implementation, decisions here will either make the business scalable and profitable, or crash prone with no customers. This interchange works both ways - It is also the duty of the technical founder to inform the business when they have absolutly lost their minds with technically infeasible fever dreams. The technical founder works with the CEO by managing development commitments and explaing tradeoffs and impacts. The business needs an antenna on Mars ? that will impact timelines somewhat

There’s more to gardening then fresh fruit

While technical in nature, the role of the technical founder includes much more ‘human’ skills than it’s name implies. Building a great engineering culture, keeping technical debt under control all while keeping business needs in mind is tough, but deeply satisfying. For a developer, it’s the best thing out there to further your craft and make yourself more valuable and well traveled.


Checkout All Posts