190 likes | 212 Vues
EEC-492/693/793 iPhone Application Development. Lecture 19 Wenbing Zhao & Nigamanth Sridhar. Outline. Unit Testing Objective-C runtime Demos Assignment: Build the temperature converter app with unit testing Build the RuntimeFun app. What Are Unit Tests?.
E N D
EEC-492/693/793iPhone Application Development Lecture 19 Wenbing Zhao & Nigamanth Sridhar EEC492/693/793 - iPhone Application Development
Outline • Unit Testing • Objective-C runtime • Demos • Assignment: • Build the temperature converter app with unit testing • Build the RuntimeFun app EEC492/693/793 - iPhone Application Development
What Are Unit Tests? • Test specific areas of functionality • Minimal external dependencies • Run frequently during development EEC492/693/793 - iPhone Application Development
Who Writes Unit Tests? • You (developers) do! • Ideally written along with new code • Test-driven development • Write tests first • Fill in the implementation until tests pass • Repeat for any new changes EEC492/693/793 - iPhone Application Development
Running Unit Tests • Automate so you don't have to explicitly run tests • Many testing frameworks can run tests every time you build • Just as compiler checks syntax, unit tests check semantics EEC492/693/793 - iPhone Application Development
Why Unit Tests? • Fewer bugs • More confidence that you're shipping a high quality product • Find bugs early • Bugs are easier (and cheaper) to fix early in development • Avoid regressions • Ensure that changing one piece of code doesn't break another • Document your code • How is a method intended to be used? Check out the tests... • Encourage good design • Spaghetti code is hard to test! Design with testability in mind EEC492/693/793 - iPhone Application Development
Unit Testing Frameworks • OCUnit for Objective C • Ships with Mac OS X developer tools, integrates with Xcode • Included with iPhone SDK as of 2.2 EEC492/693/793 - iPhone Application Development
Basics of OCUnit • SenTestCase is abstract test case superclass • Automatically runs methods that begin with “test” • Macros for asserting conditions during tests • STAssertEquals(value_1, value_2, failure_description, ...) • STAssertNotNil(object, message, ...) • STAssertTrue(expression, message, ...) • STAssertFalse(expression, message, ...) • STAssertThrowsSpecific(expression, exception, message, ...) • STAssertNoThrow(expression, message, ...) • -setUp and -tearDown methods run before and after each test EEC492/693/793 - iPhone Application Development
Adding OCUnit to an Xcode Project 1. Launch the first project 2. Create a new target to run the tests • Choose "Project > New Target" and select the “Cocaa Touch->Unit Test Bundle" target type. After clicking the "Next" button, name the new target "Test" Click the "Finish" button • Right-click the “Test” target, add the original project target as direct dependent (by clicking on the + sign and select the target) 3. Add the OCUnit framework • Right-click Frameworks folder and choose “Add > Existing Frameworks” Select the /Developer/Library/Frameworks/SenTestingKit.framework, then click the Add button • Right-click SenTestingKit.framework and choose “Get Info”, check the “Test” target, and uncheck the original project test EEC492/693/793 - iPhone Application Development
Adding OCUnit to an Xcode Project 4. Create a new group for the test cases • Add a new group “Test Cases” 5. Creating a test case • Right-click on the “Test Cases” group, select “Add > New File” • Select “Cocoa Touch class > Objective-C test case class”, then click “next” button • Name the test case in the following dialog • Make sure to uncheck the “Test” target and uncheck the original project target before you click “Finish” button EEC492/693/793 - iPhone Application Development
Adding OCUnit to an Xcode Project • 6. Unless you want to do application-level unit testing, remove all #if #else for USE_APPLICATION_UNIT_TEST in the generated source code for the unit test EEC492/693/793 - iPhone Application Development
Defining A New Test Case Class #import <SenTestingKit/SenTestingKit.h> @class Foo; @interface FooTests : SenTestCase { Foo *foo; } @end EEC492/693/793 - iPhone Application Development
Preparing Tests @implementation FooTests - (void)setUp { // Every test will have its own Foo instance foo = [[Foo alloc] init]; } - (void)tearDown { [foo release]; } ... @end EEC492/693/793 - iPhone Application Development
Adding Tests @implementation FooTests ... - (void)testCreateFoo { STAssertNotNil(foo, @"Couldn't create Foo"); } - (void)testSetBar { Bar *bar = ...; foo.bar = bar; STAssertEqualObjects(foo.bar, bar, @"Couldn't set foo.bar"); } ... @end EEC492/693/793 - iPhone Application Development
Testing Error Conditions @implementation FooTests ... - (void)testOutOfBoundsAccess { STAssertNil([foo barAtIndex:99], @"Index 99 should be nil"); } ... @end EEC492/693/793 - iPhone Application Development
Unit Testing Philosophy • Keep tests short, lightweight, fast • Test individual methods, not end-to-end behavior • Find a new bug? Write a new test before you fix it • The benefit could still be huge even if only a subset of behavior can be unit-tested • Networking • Databases EEC492/693/793 - iPhone Application Development
Objective-C Runtime • How does OCUnit find all the methods that begin with “test”? • Any other cool tricks? EEC492/693/793 - iPhone Application Development
/usr/include/objc • <objc/objc.h> • id, Nil, nil, BOOL, YES, NO • <objc/message.h> • objc_msgSend() and friends • <objc/runtime.h> • Inspect and manipulate classes, protocols, methods • Add and replace methods at runtime http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html EEC492/693/793 - iPhone Application Development
Inspecting Methods & Properties • Copy all methods for a classj // get instance methods Method *class_copyMethodList( Class cls, unsigned int *outCount); // get class methods, use: class_copyMethodList(object_getClass(cls), &count). • Get attributes for a method SEL method_getName(Method m); IMP method_getImplementation(Method m); char *method_copyReturnType(Method m); ... • Get properties for a class objc_property_t *properties = class_copyPropertyList(Class cls, unsigned int *outCount); EEC492/693/793 - iPhone Application Development