1. Trang chủ
  2. » Công Nghệ Thông Tin

Lập trình Wrox Professional Xcode 3 cho Mac OS part 70 pdf

8 156 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

at run time. The settings instruct the loader to fi rst load a special unit test framework into the application ’ s address space. This process is known as bundle injection . The testing framework causes your unit test bundle to also be loaded into memory. Initialization code in your unit test bundle intercepts the execution of your application, preventing it from running normally. Instead, the unit test bundle ’ s code links directly to the functions defi ned in the application and executes all of the tests. It then forces the application to terminate. However convoluted, the beauty of this process is that your unit tests will test the actual, binary code of your application; the same code that will run when your application launches normally. The disadvantage is that this process is complex and requires a number of concessions from your application. Mostly these are restrictions on how your application is built. In the case of some C/C++ applications, you are also required to add code to your application to support dependent unit testing. iPhone Unit Tests The iPhone SDK supports unit testing too. The techniques are very similar to the Objective - C unit testing under Mac OS X — in fact, they both use the same testing framework — but with the following differences: Independent unit tests are called logic tests in iPhone parlance, and are executed using the iPhone simulator. Dependent unit tests are called application tests in iPhone parlance, and are preformed on an actual iPhone or iPod Touch. Setting up an application test suite for the iPhone is signifi cantly different than setting up a dependent test suite for a Mac OS X application. Except for those confi guration differences, you can follow the guidelines and instructions for writing Objective - C unit tests when developing for the iPhone, substituting the terms “ independent test ” and “ dependent test ” with “ logic test ” and “ application test. ” iPhone unit testing requires iPhone OS 3.0 or later. ADDING A UNIT TEST TARGET The fi rst step in adding unit testing to a project is to create a unit test target. Choose Project ➪ New Target and choose a Unit Test Bundle template. Choose the Unit Test Bundle template from the Carbon group to test a C/C++ product, from the Cocoa group to test a Mac OS X Objective - C product, or from the Cocoa Touch group to create an iPhone unit test. An example is shown in Figure 20 - 2. ➤ ➤ ➤ Adding a Unit Test Target ❘ 553 c20.indd 553c20.indd 553 1/22/10 4:16:44 PM1/22/10 4:16:44 PM Download at getcoolebook.com 554 ❘ CHAPTER 20 UNIT TESTING Some releases of the Xcode Development Tools, particularly those intended for iPhone development, do not include the older Carbon and C++ target templates, so your installation might not have a Carbon Unit Test Bundle template. You can “borrow” one from an older Xcode installation or try installing the Mac OS X Xcode package. Give the target a name and select the project it will be added to. Choose a name that refl ects the subject of the test. For example, if you were writing tests for a target named HelperTool , you might name the unit test target HelperToolTests . FIGURE 20 - 2 Xcode creates a new unit test target and adds it to your project. You now need to confi gure it properly and populate it with tests. How you confi gure your unit test target depends on what kind of unit test it is and what kind of product it tests. You might be anxious to try out your new unit test target, but you can ’ t until it is confi gured and you have added at least one test; a unit test bundle will fail if it doesn ’ t contain any tests. The “ Creating a Unit Test ” section, later in this chapter, tells you how to add tests to your unit test bundle. c20.indd 554c20.indd 554 1/22/10 4:16:50 PM1/22/10 4:16:50 PM Download at getcoolebook.com Unit Test Target Dependencies Unit tests are part of the build process. Target dependencies are used to integrate unit tests into your build. What target dependencies you create (if any) will depend on the kind of unit test you are creating. Independent Unit Test Dependencies Because independent/logic unit tests are self - contained, they do not (technically) need to be dependent on any other targets. All of the code that needs to be tested will be compiled when the target is built. Whenever you want to run your unit tests, simply build your unit test target. One of the main tenets of test driven development is that your unit tests should be performed automatically every time you build your project. To do that, follow these steps: 1. Set the active target to your application target. 2. Make your application target dependent on your unit test target. Now every time you build your application, Xcode will fi rst build and run all of the unit tests. Alternatively, you could make the unit test target dependent on your application target; then you have the choice of just building your application or building your application and running all of your unit tests. You could also leave your application and unit test targets independent of each other and create an aggregate target that builds both. As you can see, independent unit test targets are pretty fl exible. Dependent Unit Test Dependencies Dependent Mac OS X (but not iPhone) unit test targets must depend on the target, or targets, that produce the products they test. Otherwise, there is no guarantee that the tests will be performed on up - to - date code. If you want unit tests run every time you build your product, follow these steps: 1. Set the active target to the unit test target. 2. Set the active executable to the results of the product target. Now every time you build, the application is built followed by a run of all of the unit tests. The build will only be successful if both the build and the unit tests pass muster. Using this arrangement, you can easily ignore unit tests by building just the product target, or making another target dependent on the product target directly. In a project with many product and unit test targets you could, for example, create two aggregate targets: one that depends on all of the product targets for “ quick ” builds and a second that depends on all of their respective unit test targets for “ full ” builds. An iPhone unit test target ’ s dependencies are inverted from those used by dependent unit test targets. The section “ Confi guring an iPhone Application Test ” shows you both how to confi gure the iPhone application unit test target and set up its dependencies. Adding a Unit Test Target ❘ 555 c20.indd 555c20.indd 555 1/22/10 4:17:00 PM1/22/10 4:17:00 PM Download at getcoolebook.com 556 ❘ CHAPTER 20 UNIT TESTING Confi guring an Independent/Logic Unit Test Independent unit tests require no special confi guration. All you need to do is make the source code for both the tests and the code to be tested members of the target. The compiler build settings for the target should match those of your product target as closely as possible, so that the code produced when you ’ re compiling the unit test target is as close as possible to the code that will be compiled into your fi nal product. Add the source fi les to the target by dragging them into the Compile Sources phase of the unit test target, or by opening their Info window and adding them to the unit test target in the Targets tab. You can add the source for the actual tests in a similar manner (if the tests already exist), or by adding them to the unit test target when you create them. The section “ Creating a Unit Test ” shows how to write and add a new test. The target SDK for an iPhone logic test (independent unit test) must be set to the iPhone Simulator. Confi guring a Mac OS X Dependent Unit Test A dependent unit test needs to know where to load the application or libraries to be tested. Testing an Application For applications, you accomplish this by setting the Bundle Loader ( BUNDLE_LOADER ) and Test Host ( TEST_HOST ) build settings. These should both be set to the executable you want to test. Follow these steps to quickly set both values: 1. In the Info window of the unit test target, select the Build tab. Choose All Confi gurations. Arrange the windows so that Groups & Files list in the project window and the Info window are both visible. Expand the Products group in the project source group. 2. In the target ’ s Info window, fi nd the Bundle Loader setting — you ’ ll fi nd it in the Linking group — and click in its value fi eld to edit it. In the Products smart group, locate the executable product to be tested and drag it into the value fi eld of the Bundle Loader setting. Xcode inserts the full path to the executable. For application bundles, you need to locate the application ’ s binary executable — the folder with the extension .app is not an executable. You can manually supply the path, or follow these steps: a. Right/Control+click the product and choose Reveal in Finder. b. In the Finder, Right/Control+click the application and choose Open Package Contents. c. Open the Contents folder. d. Open the MacOS folder. e. Drag the application ’ s executable into the Bundle Loader setting ’ s value cell in the target ’ s Info window. 3. Select the beginning of the path that represents the build location for the current build confi guration. Typically this is / path/to/project - folder /build/ build - configuration name / , but it may be different if you have altered the default build locations. Replace c20.indd 556c20.indd 556 1/22/10 4:17:01 PM1/22/10 4:17:01 PM Download at getcoolebook.com this portion of the path with the $(CONFIGURATION_BUILD_DIR) macro. In a project that produces a simple command - line executable, the fi nal Test Host path will look like $(CONFIGURATION_BUILD_DIR)/ ProgramName . For a bundled Cocoa or Carbon application, the Bundle Loader path will look something like $(CONFIGURATION_BUILD _DIR)/ AppName .app/Contents/MacOS/ AppName . 4. Locate the Test Host setting — you ’ ll fi nd it in the Unit Testing group — and double - click its value fi eld to edit it. Enter $(BUNDLE_LOADER) as its value. This sets the TEST_HOST build setting to the same value as the BUNDLE_LOADER setting. The Bundle Loader setting tells the linker to treat the executable as is if were a dynamic library. This allows the tests in the unit test bundle to load and link to the classes and functions defi ned in your application. The Test Host setting tells the unit test target ’ s script phase the executable that will initiate testing. When testing an application, it is the application that gets loaded and launched. The injected testing framework and bundle intercepts the application ’ s normal execution to perform the tests. Preparing Your Application A few concessions are required of applications being tested by dependent unit test bundles. You must make these changes in the target that produces your application, not the unit test target. These requirements do not apply to independent unit tests or when you ’ re testing dynamic libraries or frameworks. Open the Info window for the application target and choose the Build tab. Choose All Confi gurations and set the following: Set ZeroLink to NO (uncheck the box). If your project is a C++ program, fi nd the Symbols Hidden By Default setting and turn it off (uncheck the box). ZeroLink must be turned off for your application. The ZeroLink technology is incompatible with the techniques used to intercept the application at run time. ZeroLink has been deprecated in Xcode 3, so you may not even see it in your build settings, but projects from prior versions of Xcode may still have it set. The Symbols Hidden By Default option must be disabled for C++ applications so that all of the classes and functions defi ned by your application appear as external symbols. The unit test target must link to the symbols in your application, so these symbols must all be public. Objective - C tests are all resolved at run time by introspection, so they don ’ t require any public symbols at link time. Testing Libraries and Frameworks When you ’ re constructing a unit test to test a dynamic library or framework, leave the Bundle Loader and Test Host settings empty. This is because the “ program ” to be loaded for testing will be the unit test bundle itself. If the Test Host setting is blank, the script launches the otest (for Objective - C) or CPlusTestRig (for C/C++) tool instead. The testing tool loads the unit test bundle and runs the tests it fi nds there, with the assumption that the unit test bundle either contains (in the ➤ ➤ Adding a Unit Test Target ❘ 557 c20.indd 557c20.indd 557 1/22/10 4:17:01 PM1/22/10 4:17:01 PM Download at getcoolebook.com 558 ❘ CHAPTER 20 UNIT TESTING case of independent tests) or will load (in the case of dependent tests for libraries and frameworks) the code to be tested. For dependent unit tests that test libraries or frameworks, the unit test bundle is the client application. Confi gure your unit test bundle exactly as you would an application that uses those libraries or frameworks, adding the frameworks to the target and including whatever headers are appropriate to interface to them. The dynamic library loader takes care of resolving the references and loading the libraries at run time. Confi guring an iPhone Application Test Testing an iPhone application is different from testing a Mac OS X application, and requires a different organization in Xcode. In Mac OS X development (described in the previous section), you tell the unit test bundle what product you want tested. It takes on the responsibility of loading that target, injecting itself into the application, and performing its tests. In iPhone development, the roles of the application and unit test bundle are reversed. You create a custom version of your application that includes the unit test bundle product. You load and run your test app on your iPhone or iPod Touch device like any other app. Once started, your app loads the unit test bundle, which takes over and performs its tests. To confi gure an iPhone app for unit testing, follow these steps: 1. Add a Unit Test Bundle target, using the Cocoa Touch Unit Test Bundle template, as described in the beginning of this section. This is your unit test target . 2. Duplicate the target that builds your app. Give it a descriptive name like MyAppTesting. This is your test app target . 3. Make your test app target dependent on your unit test bundle target. 4. Add the product of the unit test target (the MyTests.octest bundle) to the Copy Bundle Resources phase of your test app target. This will include the compiled suite of unit tests in your app ’ s resource bundle. 5. Set the active target to the test app target. 6. Set the Target SDK to iPhone Device 3.0 or later. 7. Build and run your test app target. The test results will appear in your console window. Unlike all other kinds of unit tests, iPhone application tests aren ’ t run during the build phase. You must build and run your test application, which downloads both it and the unit tests to your iPhone for execution. This introduces a number of limitations to using iPhone application tests: Application tests can ’ t be made an automatic part of your build process. The application test bundle must also be provisioned to run on your iPhone. The “ correct ” way to do this is to create a provisioning profi le that includes both the application and the application test bundle (see Chapter 22). I admit that I ’ ll often simply set the Bundle ➤ ➤ c20.indd 558c20.indd 558 1/22/10 4:17:02 PM1/22/10 4:17:02 PM Download at getcoolebook.com Identifi er build setting in the unit test bundle to the same ID as the application. It seems sleazy, but it works. The unit test bundle will take over your app, run its tests, and exit. You can ’ t use your application interactively during testing. The code in the unit test can ’ t link directly to the application. This is because the unit test target builds before the application, so it can ’ t link directly to the application ’ s code. You might be scratching your head about the last one. You ’ re probably asking “ If the unit test code can ’ t link to the code in the application, what use is it? ” One solution is to include the code in both targets. At run time only one implementation of the class will be used — most likely the one in the application (because it loaded fi rst), but the Objective - C run time doesn ’ t specifi cally guarantee this. Regardless, this is an acceptable solution in most cases and gives your unit tests direct access to iPhone hardware and its application environment. Another solution is introspection. Instead of referring to application classes directly, do it indirectly in the case where the test will be running on an actual iPhone. Listing 20 - 1 shows an example. This code will compile, link, and run — as long as something in the same process actually implements the SieveOfEratosthenes class, which our application does. LISTING 20 - 1: Using soft class references in an iPhone application test - (void)setUp { #if TARGET_OS_IPHONE testSieve = [[NSClassFromString(@"SieveOfEratosthenes") alloc] initWithMaxPrime:UNIT_TEST_MAX_PRIMES]; #else testSieve = [[SieveOfEratosthenes alloc] initWithMaxPrime:UNIT_TEST_MAX_PRIMES]; #endif STAssertNotNil(testSieve,@"Unable to create SieveOfEratosthenes"); } The most signifi cant pitfall in iPhone application testing is the same problem inherent in logic tests (independent unit tests). Namely, that you run the risk of testing code that’s different from the code in your fi nal product. You must remember to update your test app target scrupulously so that it has the same build confi guration as your primary app target. If you have any doubts, simply discard the test app target and reproduce it using the steps listed previously. This will guarantee that all of your test app target settings are identical to those in your production app target. ➤ ➤ Adding a Unit Test Target ❘ 559 c20.indd 559c20.indd 559 1/22/10 4:17:03 PM1/22/10 4:17:03 PM Download at getcoolebook.com 560 ❘ CHAPTER 20 UNIT TESTING CREATING A UNIT TEST Once you have your unit test target created and confi gured, adding unit tests is simple. Here are the basic steps: 1. Create a unit test class and add its source fi le to the unit test target. 2. Add test methods to the class. 3. Register the tests with the unit testing framework. Unit test class fi les can go anywhere in your project, but I suggest, at the very least, creating a group for them named “ Tests ” or “ Unit Tests. ” In a larger project you might organize your unit test fi les in a folder, or you might group them together with the code that they test. The choice is yours. Each class that you create defi nes a group of tests. Each test is defi ned by a test method added to the class. A class can contain as many different tests as you desire, but must contain at least one. How you organize your tests is entirely up to you, but good practices dictate that a test class should limit itself to testing some functional unit of your code. It could test a single class or a set of related functions in your application. Once defi ned, your tests must be registered with the unit testing framework so that it knows what tests to run. For Objective - C tests this happens automatically. Objective - C test methods must adhere to a simple naming scheme — basically they must all begin with the name “ test. ” Objective - C introspection is then used to locate and run all of the tests you defi ned. For C++ unit tests, you add a declaration for each test you ’ ve written. The exact requirements for each are described in the “ Objective - C Tests ” and “ C++ Test Registration ” sections, respectively. Each test method should perform its test and return. Macros are provided for checking the expectations of each test and reporting failures. A test is successful if it completes all of its tests and returns normally. An example test is shown in Listing 20 - 2. LISTING 20 - 2: Sample C++ unit test void SieveOfEratosthenesTests::testPrimes( ) { // Test a number of known primes static int knownPrimes[] = { 2, 3, 5, 11, 503, 977, 12347, 439357, 101631947 }; SieveOfEratosthenes testSieve(UNIT_TEST_MAX_PRIMES); for (size_t i=0; i < sizeof(knownPrimes)/sizeof(int); i++) CPTAssert(testSieve.isPrime(knownPrimes[i])); } In this example, the testPrime function defi nes one test in the SieveOfEratosthenesTests class. The test creates an instance of the SieveOfEratosthenes class, and then checks to see that it correctly identifi es a series of numbers known to be prime. If all of the calls to testSieve.isPrime() return true , the test is successful; the testPrimes object is destroyed and the function returns. If any call to testSieve.isPrime() returns false , the CPTAssert macro signals to the testing framework that the test failed. The testing macros are described in the “ Objective - C Test Macros ” and “ C++ Test Macros ” sections. c20.indd 560c20.indd 560 1/22/10 4:17:08 PM1/22/10 4:17:08 PM Download at getcoolebook.com . void SieveOfEratosthenesTests::testPrimes( ) { // Test a number of known primes static int knownPrimes[] = { 2, 3, 5, 11, 5 03, 977, 1 234 7, 439 357, 101 631 947 }; SieveOfEratosthenes testSieve(UNIT_TEST_MAX_PRIMES); . false , the CPTAssert macro signals to the testing framework that the test failed. The testing macros are described in the “ Objective - C Test Macros ” and “ C++ Test Macros ” sections. c20.indd. can “borrow” one from an older Xcode installation or try installing the Mac OS X Xcode package. Give the target a name and select the project it will be added to. Choose a name that refl ects the subject

Ngày đăng: 04/07/2014, 06:20

Xem thêm: Lập trình Wrox Professional Xcode 3 cho Mac OS part 70 pdf

TỪ KHÓA LIÊN QUAN