An introduction to the motivation and theory behind test-driven development, suitable for people with experience in Mac or iOS development using Objective-C.
4. Software engineering:
goals
• Make money (sometimes)
• …by making software that the customer
wants
5. Software engineering:
goals
• Make money (sometimes)
• …by making software that the customer
wants
• Satisfies the customer’s requirements
6. Software engineering:
goals
• Make money (sometimes)
• …by making software that the customer
wants
• Satisfies the customer’s requirements
• Without costing too much to make
7. Testing: goals
Cost Time Detected
• validate code
behaviour Time Requirements Architecture Construction
System After
introduced Test Release
• discover defects Requirements 1 3 5-10 10 10-100
• verify fixes
Architecture - 1 10 15 25-100
• detect regressions
Construction - - 1 10 10-25
8. Testing: goals
Cost Time Detected
• validate code
behaviour Time Requirements Architecture Construction
System After
introduced Test Release
• discover defects Requirements 1 3 5-10 10 10-100
• verify fixes
Architecture - 1 10 15 25-100
• detect regressions
Construction - - 1 10 10-25
Unit Testing
9. The testing landscape
System Test Beta/Acceptance Testing
Black Box
Pen/fuzz Testing
Integration Testing GUI Testing
Grey Box
Unit testing (undirected)
Source Audit
White Box
Class Component System
Static Analysis, Debugging
10. The testing landscape
System Test Beta/Acceptance Testing
Black Box
Pen/fuzz Testing
Integration Testing GUI Testing
TDD
Grey Box
Unit testing (undirected)
Source Audit
White Box
Class Component System
Static Analysis, Debugging
12. TDD - what it achieves
• Imposes black-box thinking for the
developer
13. TDD - what it achieves
• Imposes black-box thinking for the
developer
• Guides design and implementation
14. TDD - what it achieves
• Imposes black-box thinking for the
developer
• Guides design and implementation
• YAGNI
15. TDD - what it achieves
• Imposes black-box thinking for the
developer
• Guides design and implementation
• YAGNI
• Provides a safety net for future
development
16. TDD - what it achieves
• Imposes black-box thinking for the
developer
• Guides design and implementation
• YAGNI
• Provides a safety net for future
development
• Assists accurate planning
21. TDD != [bullet
silverColor]
• Can’t ensure the developer understood
requirements
• Or that the requirements remained static
22. TDD != [bullet
silverColor]
• Can’t ensure the developer understood
requirements
• Or that the requirements remained static
• Doesn’t guarantee successful integration
23. TDD != [bullet
silverColor]
• Can’t ensure the developer understood
requirements
• Or that the requirements remained static
• Doesn’t guarantee successful integration
• Takes time (mainly to write)
24. TDD != [bullet
silverColor]
• Can’t ensure the developer understood
requirements
• Or that the requirements remained static
• Doesn’t guarantee successful integration
• Takes time (mainly to write)
• Must actually be run to add value
30. What do I test?
• Anything, within reason
• Mustn’t take too long to run the tests
• Shouldn’t depend on the environment
• database, filesystem, network
• …not that those tests aren’t important
31. What do I test?
• Anything, within reason
• Mustn’t take too long to run the tests
• Shouldn’t depend on the environment
• database, filesystem, network
• …not that those tests aren’t important
• Evaluate risk
• known-buggy classes
• “hard” code
32. When do I test?
• Whenever you make a change
• Whenever you want to build
33. Test Design
#import <SenTestingKit/SenTestingKit.h>
@interface DateComparisonTests : SenTestCase { One (or more) per class
- (void)setUp {
gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar];
comps = [[NSDateComponents alloc] init];
}
Common stuff here
- (void)tearDown {
[gregorianCalendar release];
gregorianCalendar = nil;
[comps release];
comps = nil;
}
- (void)testDatesOnTheSameDayAreConsideredSame {
//...
}
- (void)testCloseDatesOnSeparateDaysAreNotSame {
These all independent
short, readable, fast
//...
}
- (void)testSameDayInDifferentYearsAreNotTheSame {
//...
}
37. Testable code
• Small, focussed classes, that contain…
• Short methods, each with obvious effect
38. Testable code
• Small, focussed classes, that contain…
• Short methods, each with obvious effect
• Any side-effects are few and easy to predict
39. Testable code
• Small, focussed classes, that contain…
• Short methods, each with obvious effect
• Any side-effects are few and easy to predict
• Helper data passed in, not discovered
40. Testable code
• Small, focussed classes, that contain…
• Short methods, each with obvious effect
• Any side-effects are few and easy to predict
• Helper data passed in, not discovered
• Low “cyclomatic complexity”
41. Testable code
• Small, focussed classes, that contain…
• Short methods, each with obvious effect
• Any side-effects are few and easy to predict
• Helper data passed in, not discovered
• Low “cyclomatic complexity”
i.e. an end to
@interface GodClass : UIViewController
47. OCUnit and the Device
Source: http://developer.apple.com/iphone/library/documentation/Xcode/Conceptual/
iphone_development/135-Unit_Testing_Applications/unit_testing_applications.html
OK, so open source projects don&#x2019;t always make money. But they&#x2019;re usually trying to satisfy a need still, and must make efficient use of the resources available.
OK, so open source projects don&#x2019;t always make money. But they&#x2019;re usually trying to satisfy a need still, and must make efficient use of the resources available.
OK, so open source projects don&#x2019;t always make money. But they&#x2019;re usually trying to satisfy a need still, and must make efficient use of the resources available.
OK, so open source projects don&#x2019;t always make money. But they&#x2019;re usually trying to satisfy a need still, and must make efficient use of the resources available.
The main point of testing is _not_ to find bugs, but to show that the code does what it ought. Finding bugs is secondary, hence tests &#x201C;failing&#x201D; when there are bugs not the other way around. It should reduce engineering costs by making it more likely for v1.0 to be accepted by the customer.
The main point of testing is _not_ to find bugs, but to show that the code does what it ought. Finding bugs is secondary, hence tests &#x201C;failing&#x201D; when there are bugs not the other way around. It should reduce engineering costs by making it more likely for v1.0 to be accepted by the customer.
The main point of testing is _not_ to find bugs, but to show that the code does what it ought. Finding bugs is secondary, hence tests &#x201C;failing&#x201D; when there are bugs not the other way around. It should reduce engineering costs by making it more likely for v1.0 to be accepted by the customer.
The main point of testing is _not_ to find bugs, but to show that the code does what it ought. Finding bugs is secondary, hence tests &#x201C;failing&#x201D; when there are bugs not the other way around. It should reduce engineering costs by making it more likely for v1.0 to be accepted by the customer.
The main point of testing is _not_ to find bugs, but to show that the code does what it ought. Finding bugs is secondary, hence tests &#x201C;failing&#x201D; when there are bugs not the other way around. It should reduce engineering costs by making it more likely for v1.0 to be accepted by the customer.
So unit testing can be seen as either white or black box, depending on how you organise it. Almost all other testing methods are black box and investigate the whole system, a really expensive way to find bugs.
So unit testing can be seen as either white or black box, depending on how you organise it. Almost all other testing methods are black box and investigate the whole system, a really expensive way to find bugs.
So unit testing can be seen as either white or black box, depending on how you organise it. Almost all other testing methods are black box and investigate the whole system, a really expensive way to find bugs.
You don&#x2019;t want to be this guy. More importantly, you don&#x2019;t want to be the guy who caused this.
Now we don&#x2019;t know what caused this.
I want to tell a quick story here... not to pick on Microsoft but I find this story entertaining.
December 31, 2008 all Zunes hang on last day of leap year. Problem was code that assumed 365 days in the year - something a seasoned unit-tester would have thought of before even writing the code.
Start by writing failing tests. Then make them pass. Then clean up. Dave&#x2019;s/Uncle Bob&#x2019;s Boy Scout Rule.
Start by writing failing tests. Then make them pass. Then clean up. Dave&#x2019;s/Uncle Bob&#x2019;s Boy Scout Rule.
Start by writing failing tests. Then make them pass. Then clean up. Dave&#x2019;s/Uncle Bob&#x2019;s Boy Scout Rule.
Make sure your unit test suite only takes a few seconds to run, so you can run it on a whim without losing mental focus. Make your build dependent on the test target, so failed tests = no build. Continuous Integration.
GHUnit also has -setUpClass and -tearDownClass, could be useful for speed. However, your tests should assume that nothing else has happened - this instance of the class could have run other tests, it might not have.
If your unit tests rely on a data oracle, make sure it always gives the same results. Random failures are never fun, and fuzzing can be done as a separate activity.
Reinforces the red/green nature of testing, although the presentation of failures is a little obtuse.
Suck. Note that in Xcode previews you can&#x2019;t actually create unit test targets. Still, the presentation of failure is much better.
Suck. Note that in Xcode previews you can&#x2019;t actually create unit test targets. Still, the presentation of failure is much better.
Suck. Note that in Xcode previews you can&#x2019;t actually create unit test targets. Still, the presentation of failure is much better.
Suck. Note that in Xcode previews you can&#x2019;t actually create unit test targets. Still, the presentation of failure is much better.
Note that it&#x2019;s not only Eclipse/JUnit that works better. NUnit can be made to do this with MonoDevelop.
You can run unit tests on the device, it&#x2019;s a little awkward. It doesn&#x2019;t use any interface except NSLog, so you can&#x2019;t get failures back into the IDE.
GHUnit has a nice UI, works properly on the device, and is debuggable. However it&#x2019;s harder to automate tests because they&#x2019;re running in an application so don&#x2019;t feed back to the IDE.
GHUnit has a nice UI, works properly on the device, and is debuggable. However it&#x2019;s harder to automate tests because they&#x2019;re running in an application so don&#x2019;t feed back to the IDE.
GHUnit has a nice UI, works properly on the device, and is debuggable. However it&#x2019;s harder to automate tests because they&#x2019;re running in an application so don&#x2019;t feed back to the IDE.