cocoa heads testing and viewcontrollers
TRANSCRIPT
Testing and ViewControllers
"Extract testable code” &
”Test UI without the simulator”.
Small is the beauty!
• What did I do?
• Why / When / Dangers unit test
• A way to make viewControllers testable
• Make tests visible
What did I do
• TDD at Philips, small and big projects
• Lots of legacy code refactoring
• App that could have benefitted from testing
Unit testing
Integration testing
Webservice
Local device test
External web service
VC’s Mock web service Views
UI Automation (Calabash)
Helped Developers
Helped ?
Legacy code
Legacy class
Sprout Class -> Unit tests
Unit test killers:
• Object should not set there own properties.
• Reach out to a database
• Many dependencies, no default working state on its own
Legacy code
Object should not set there own properties.
class ExampleVC: UIViewController {
let userDefaults = NSUserDefaults.standardUserDefaults() }
class ExampleVC: UIViewController {
var userDefaults : NSUserDefaults? override func viewDidLoad() { super.viewDidLoad() userDefaults = NSUserDefaults.standardUserDefaults() } }
Legacy code
Object should not set there own properties.
class ExampleVC: UIViewController {
lazy var userDefaults : NSUserDefaults = { NSUserDefaults.standardUserDefaults() }() }
Why
Tests written for code coverage are a waste of time
Write them to write better code or stop writing tests!
When
Data Manipulation Draw view
What value should I use for this IndexPath?
I need a list but I have an object with properties
ViewController
Different states
Do not test animations wait for the result
Only visible for a very peculiar state
What value should I use for this IndexPath?
Data Manipulation
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TableViewCell
cell.label.text = self.datasource.item(indexPath) return cell }
func testThenHasMessageAndDescription() { let resultMessage = tableDatasource.item(NSIndexPath(forItem: 0, inSection: 0)) let resultDescription = tableDatasource.item(NSIndexPath(forItem: 1, inSection: 0)) XCTAssertEqual(resultMessage!, “Message”) XCTAssertEqual(resultDescription!, “Description”) }
Data Manipulation
I need a list but I have an object with properties
let fakeResponse = ["message" : "bla", "description" : "bla some more"] self.mappedArray = [fakeResponse["message"]!, fakeResponse["description"]!]
func item(indexPath: NSIndexPath) -> (String?){
if indexPath.item < mappedArray?.count{ return mappedArray![indexPath.item] }else{ return nil } }
Do not test animations wait for the result
Draw view
- (void)testThenShowInPlaceError { XCTestExpectation *exp = [self expectationWithDescription:@"Wait for error view"]; self.viewController.viewControllerDatasource.respondWithError = YES;
[self.viewController.viewControllerDatasource reloadDataWithDataCompletion:nil failure:nil animationCompletion:^(BOOL success) {
FBSnapshotVerifyView(self.viewController.view, @""); [exp fulfill]; }];
NSTimeInterval animationTime = kAsyncViewsAnimationDuration; [self waitForExpectationsWithTimeout:animationTime + 2 handler:nil]; }
Only visible for a very peculiar state
Draw view
- (void)testThenOfferCancel { id vc = OCMPartialMock([[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"ViewController"]); //We only use a stub here to have faster test cycles OCMStub([vc async_timeOutDelay]).andReturn(@1); XCTestExpectation *exp = [self expectationWithDescription:@"Wait for cancel"]; [self offerCancelOnViewController:vc exp: exp test:^(XCTestExpectation *exp) { FBSnapshotVerifyView(((UIViewController *) vc).view, @""); [vc stopMocking];
[exp fulfill]; }]; }
Make VC’s Testable
Data Manipulation Draw view
ViewController
SharedAsyncDatasource UIViewController+Async
Make Tests visible
Tests are just as important as application code. Why not put them together?
Jon Reid
Inspiration
• http://qualitycoding.org/xcode-unit-testing/
• http://iosunittesting.com
• https://github.com/Inferis/IIAsyncViewController
• “Working Effectively with Legacy Code “ Michael Feathers