Re: TDD and Xcode
Chris Hanson <[email protected]> Sat, 11 Oct 2008 19:16:20 -0700
| Newsgroups | gmane.comp.programming.test-driven-development,gmane.comp.programming.test-first-user-interfaces |
|---|---|
| Message-ID | <[email protected]> |
On Oct 11, 2008, at 7:41 AM, ae1fric wrote:
> I'm considering switching my development over to Python under=20=20
> Apple's Xcode development
> environment -- Cocoa and Core Data provide some significant=20=20
> advantages for the type of
> development I'm doing. The one downside so far is that I haven't=20=20
> been able to figure out how
> to do TDD under Xcode.
>
> Has anyone successfully developed with TDD under Xcode?
Yes, there are actually quite a few people who do extensive TDD on Mac=20=20
OS X using Cocoa and Xcode =97 it turns out that it's actually a great=20=20
platform for doing so, thanks to its objects-all-the-way-down (for the=20=20
most part) design.
Disclosure: I work on the Xcode IDE at Apple, but I don't speak for=20=20
Apple or the Xcode team here or anywhere else online. (Unless you=20=20
happen to be watching me in a WWDC video...)
> I think the reason I'm struggling to see how to do TDD under Xcode=20=20
> is that the development
> environment does so much automagically: you define your data model,=20=20
> draw your user
> interface, define bindings between the data model and the UI and=20=20
> suddenly you have a
> working application, without writing a line of code.
>
> Any suggestions for using TDD in this kind of environment would be a=20=20
> great help.
You can actually still do TDD by writing unit tests that specify how=20=20
your data model should be set up, and how the connections between=20=20
objects in your nib files -- whether those connections are target-=20
action, outlet or binding connections -- are set up.
Virtually everything that Xcode, Interface Builder and Cocoa let you=20=20
do graphically can also be done in code. None of them *generate* code=20=20
to do so, but the objects that are created are the same either way, so=20=20
you can just introspect them.
For example, lets say you want to create a View Controller that has a=20=20
Name field for an employee, which is managed by an object controller. =
=20
You can then write a test that you want the NSTextField's value=20=20
binding to be connected to the "selection" controller key path and=20=20
"name" model key path of an Employee Controller.
Here's an Objective-C test method that does just that written against=20=20
Sen:te's OCUnit framework that's included with Xcode. Using PyObjC=20=20
and a Python testing framework should be the similar except for the=20=20
obvious differences in syntax and setting up your own type of test rig.
// This assumes viewController has nameField and employeeController=20=20
properties for
// the appropriate IBOutlets, and is configured in -setUp
- (void)testNameFieldBoundToName {
NSDictionary *info =3D [viewController.nameField=20=20
infoForBinding:NSValueBinding];
STAssertNotNil(info,
@"The name field's value should be bound.");
NSObjectController *observedController =3D [info=20=20
objectForKey:NSObservedObjectKey];
STAssertEquals(observedController,=20=20
viewController.employeeController,
@"The name field's value should be bound to the employee=20=20
controller.");
NSString *observedKeyPath =3D [info=20=20
objectForKey:NSObservedKeyPathKey];
STAssertEqualObjects(observedKeyPath, @"selection.name",
@"The name field's value should be bound to the 'selection'=20=20
controller key "
@"and 'name' model key of the employee controller.");
}
I've usually created my own assertion macros to encapsulate this kind=20=20
of test, of course; then the test winds up looking like this:
- (void)testNameFieldBoundToNameWithBindingAssertionMacro {
MyAssertObjectIsBoundThroughKeyPath(viewController.nameField,=20=20
NSValueBinding,
viewController.employeeController, @"selection", @"name",
@"The employee name field should be bound to the employee=20=20
controller.");
}
Note that this isn't specifying the layout of the text field in my nib/=20
xib file, its font, or anything like that. You can specify those in=20=20
tests too, if you want to, but you don't have to. Whether you do so=20=20
or not depends on the level of detail you need for your UI and the=20=20
level of churn that it's likely to go through.
For example, if you're changing the look and feel or layout of your=20=20
UI, you might not want to specify any of that, but the above test=20=20
would probably be very useful in ensuring you don't forget to bind up=20=20
a text field you've just replaced as part of iterating on your UI=20=20
design.
On the other hand, I wrote some code a while back to create a "glass=20=20
bar" like at the bottom of the message list in Mail.app on Leopard. I=20=20
actually did write tests to specify the layout of that interface down=20=20
to the point, despite creating it in Interface Builder, because it=20=20
needed a very specific layout and I didn't want to accidentally change=20=20
it as I adjusted other things in the xib.
One other thing this doesn't do is "push" events through controls to=20=20
test that your controller-level code reacts to them. In general you=20=20
don't need to write such tests except for your own custom controls.=20=20=20
Otherwise, you can trust that controls will work correctly as long as=20=20
they're connected correctly, and just verify that those connections=20=20
are set up right in your tests.
The same philosophy can be followed when writing tests that describe=20=20
your data model. An NSManagedObjectModel can be loaded=20=20
programmatically and you can just ask for its entities and their=20=20
properties, and walk across the entire thing looking at their values=20=20
in code. That makes it very easy to TDD because you can just create=20=20
an infrastructure of tests that say "this model should have an entity=20=20
named Employee" and "the Employee entity should have a required name=20=20
attribute" and "the Employee entity should have a to-one 'department'=20=20
relationship to the Department entity, with an 'employees' inverse=20=20
relationship."
Here are some of the blog posts I've written on the topic of writing=20=20
tests for Cocoa over the years:
Xcode: Unit testing Cocoa frameworks
http://chanson.livejournal.com/119303.html
=09
Xcode: Debugging Cocoa framework unit tests
http://chanson.livejournal.com/119578.html
=09
Xcode: Unit testing Cocoa applications
http://chanson.livejournal.com/120263.html
=09
Xcode: Debugging Cocoa application unit tests
http://chanson.livejournal.com/120740.html
=09
Trust, but verify.
http://chanson.livejournal.com/118380.html
=09
Unit testing Cocoa user interfaces: Target-Action
http://chanson.livejournal.com/148204.html
=09
Unit testing Cocoa user interfaces: Cocoa Bindings
http://chanson.livejournal.com/172390.html
=09
Unit testing and Core Data
http://chanson.livejournal.com/115621.html
=09
My blog's "unit testing" tag
http://chanson.livejournal.com/tag/unit+testing
Erik Doernenburg, the creator of the useful OCMock mock-objects=20=20
framework, has also written a bit about how to use OCMock to test=20=20
Cocoa controllers.
Testing Cocoa Controllers with OCMock
http://erik.doernenburg.com/2008/07/testing-cocoa-controllers-with-ocmock/
His approach is very appropriate for testing that your own controller=20=20
objects react to changes in your UI correctly, again without having to=20=20
"push events" through controls.
Let me know if there's anything else you'd like me to write about!
And if there are any ways you'd like to see Xcode, Interface Builder,=20=20
or Cocoa improved, or if you run into any bugs or other problems,=20=20
please file an enhancement request or bug report at http://bugreport.apple.=
com/=20
=97 bugs and feature/enhancement requests go into Apple engineering's =
=20
Radar system and are evaluated by engineering and management. Even=20=20
"duplicate" bug reports are extremely useful because they can wind up=20=20
having the critical nugget of information that is needed to fix a bug=20=20
(by making it reproducible or revealing a common thread), or can be=20=20
used to gauge demand for specific kinds of enhancement.
-- Chris
[Non-text portions of this message have been removed]
------------------------------------
Yahoo! Groups Links
<*> To visit your group on the web, go to:
http://groups.yahoo.com/group/testdrivendevelopment/
<*> Your email settings:
Individual Email | Traditional
<*> To change settings online go to:
http://groups.yahoo.com/group/testdrivendevelopment/join
(Yahoo! ID required)
<*> To change settings via email:
mailto:[email protected]=20
mailto:[email protected]
<*> To unsubscribe from this group, send an email to:
[email protected]
<*> Your use of Yahoo! Groups is subject to:
http://docs.yahoo.com/info/terms/