Unit Testing

http://nshipster.com/unit-testing/


Unit Testing

Written by Mattt Thompson on May 27th, 2013

Unit Testing is an emotional topic for developers. It inspires a sense of superiority to its most zealous adherents, and evokes a feeling of inadequacy to non-practitioners. Cargo Cults like TDD stake their reputation on unit testing to the point of co-opting and conflating utility with morality.

It's as close to a religious debate as programmers get... nevermind the matter of tabs-versus-spaces.

Objective-C developers have, for the most part, remained relatively apathetic to Unit Testing ("There's that SenTest thing, but who uses that, really?"). Between static typing, typically manageable project sizes, and a compiler advanced enough to rewrite code for you, unit testing isn't as much of a necessity as it is for more dynamic languages like Ruby (at least in practice).

But that's not to say that Objective-C developers wouldn't benefit from unit testing. In fact, as Objective-C continues to become more collaborative, with growing participation in the open source community, automated testing will become a necessity.

This week NSHipster will explore the world of unit testing frameworks, and how to set up an automated build system with Travis CI.


Unit Testing is a tool, just like any other tool. Its purpose is to make us better at our jobs, which is to produce robust, maintainable software.

It's a simple enough premise: write code to construct environments that exercise the particular behavior of a given method, function, class, or feature. Variables are isolated in a scientific manner, so as to test assumptions with logical atomicity.

OCUnit

OCUnit, a.k.a. SenTestingKit, was integrated into Xcode 2.1 circa WWDC 2005, as a result of its use in the development of Core Data 1.0. Developed by Sen:te, OCUnit is actually one of the first unit testing libraries written for any language.

Unit Tests were added into a separate testing target in the Xcode Project. Each test file defines anSenTestCase subclass, which implements a series of methods beginning with the word test. Cassert-style macros are used to fail tests if the specified condition is not met. Each test is run in sequence, independently of one another, with the results logged afterwards:

#import <SenTestingKit/SenTestingKit.h>
#import "Person.h"

@interface TestPerson : SenTestCase
@end

@implementation TestPerson
- (void)testFullName {
   Person *person = [[Person alloc] init];
   person.firstName = @"Pablo";
   person.lastName = @"Picasso";
   STAssertEqualObjects([person fullName], @"Pablo Picasso", nil);
}

The SenTestingKit assertions are about what you'd expect, offering bread-and-butter equality, existence, and truth checks:

  • STAssertNil()
  • STAssertNotNil()
  • STAssertTrue()
  • STAssertFalse()
  • STAssertEqualObjects()
  • STAssertEquals()
  • STAssertEqualsWithAccuracy()
  • STAssertThrows()
  • STAssertThrowsSpecific()
  • STAssertThrowsSpecificNamed()
  • STAssertNoThrow()
  • STAssertNoThrowSpecific()
  • STAssertNoThrowSpecificNamed()
  • STAssertTrueNoThrow()
  • STAssertFalseNoThrow()
  • STFail()

And yet, as useful as tests are, they necessarily introduce friction into a development cycle. When project pressures begin to weigh, tests are the first thing to be thrown overboard. At some point, the tests stop passing ("we can worry about that later—now we have to ship!")

The only chance testing has to remain relevant in high-pressure situations is to reduce that friction in development. Essentially, tests need to become both easier to write and easier to run.

Open Source Libraries

There are a myriad of open source libraries that attempt to make testing more palatable by way of syntactic sugar and features like method stubsmock objects, and promises.

Here's a list of some of the most useful open source libraries for unit testing:

Mock Objects
OCMockErik Doernenburg 
OCMockitoJon Reid 
Matchers
ExpectaPeter Jihoon Kim 
OCHamcrestJon Reid 
BDD / TDD
SpectaPeter Jihoon Kim 
KiwiAllen Ding 
CedarPivotal Labs 
Frameworks
GHUnitGabriel Handford 

Automated Testing

Making tests easier to write is one thing, but getting them to run without affecting productivity is quite another.

Jenkins

For a long time, installing Jenkins on a dedicated Mac Mini was the state-of-the-art for automated build servers.

Aside from the fact that it's kinda the worst thing ever to set-upyou can do a lot of cool things like notifying build status over IM or IRC, automatically distributing builds to TestFlight or HockeyAppwith Shenzhen, and generating documentation with AppleDoc.

Travis

Until recently, automated unit testing for Objective-C was the privilege of projects that could dedicate the time and money to setup a CI server. Travis CI made CI available to the masses.

CI for Objective-C is more difficult than for other languages, because it needs to be done on a Mac. For economic reasons, there just isn't a market for cloud-based OS X environments like there is for Linux. Fortunately, SauceLabs has built such a virtualized Mac cloud, and is graciously donating some of it to run tests for open source Objective-C projects on Travis-CI.

For an example of automated Objective-C unit testing in the wild, check out how AFNetworking does it.

The Tests subdirectory contains separate projects for iOS and OS X targets, as well as a Podfile, which specifies all of the testing library dependencies. AFNetworking executes a Rake task, which shells out to xctool.

All of the configuration for setup is defined in .travis.yml:

.travis.yml
language: objective-c
before_install:
  - brew update
  - brew install xctool --HEAD
  - cd Tests && pod install && cd $TRAVIS_BUILD_DIR
  - mkdir -p "Tests/AFNetworking Tests.xcodeproj/xcshareddata/xcschemes" && cp Tests/Schemes/*.xcscheme "Tests/AFNetworking Tests.xcodeproj/xcshareddata/xcschemes/"
script: rake test

Full documentation for the Travis configuration file can be found on Travis-CI.org.


Once again, the direction of Objective-C has been directly influenced by the Ruby community. Those guys and gals are serious about testing. It's not like we should complain, though: betweenCocoaPodsRubyMotion, and Nomad, Ruby has made Objective-C development better by several orders of magnitude.

The bottom line is that testing has come to Objective-C. It's not always necessary, and it's certainly not a silver bullet for writing great software, but it's proven itself invaluable (especially for open source development). So give it a try now, before not testing becomes seriously uncool.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值