Download - iOS Behavior-Driven Development
![Page 1: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/1.jpg)
iOS Behavior-Driven DevelopmentTesting RESTful Applications with Kiwi and Nocilla
Brian Gesiak
March 9th, 2014
Research Student, The University of Tokyo
@modocache #startup_ios
![Page 2: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/2.jpg)
Today
• Behavior-driven development (BDD) • iOS behavior-driven development
• Kiwi • Testing asynchronous networking
• Nocilla
![Page 3: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/3.jpg)
Test-Driving Network Code
• Let’s say we want to display a user’s repositories on GitHub
• We can GET JSON from the GitHub API
• https://api.github.com/users/{{ username }}/repos.json
Motivation
![Page 4: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/4.jpg)
Test-Driving Network CodeMotivation
/// GET /users/:username/repos ![ { "id": 1296269, "name": "Hello-World", "description": "My first repo!", /* ... */ } ]
![Page 5: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/5.jpg)
Test-Driving Network CodeDemonstration
![Page 6: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/6.jpg)
Building the AppBehavior-Driven Development Using Kiwi
• Behavior-driven development (BDD) is an extension of test-driven development
![Page 7: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/7.jpg)
Test-Driven Development
![Page 8: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/8.jpg)
Test-Driven Development
• Red: Write a test and watch it fail
![Page 9: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/9.jpg)
Test-Driven Development
• Red: Write a test and watch it fail• Green: Pass the test (by writing as little code as possible)
![Page 10: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/10.jpg)
Test-Driven Development
• Red: Write a test and watch it fail• Green: Pass the test (by writing as little code as possible)• Refactor: Remove duplication
![Page 11: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/11.jpg)
Test-Driven Development
• Red: Write a test and watch it fail• Green: Pass the test (by writing as little code as possible)• Refactor: Remove duplication• Repeat
![Page 12: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/12.jpg)
Example of iOS TDD Using XCTest
![Page 13: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/13.jpg)
// Create an XCTestCase subclass @interface GHVRepoCollectionTests : XCTestCase // The subject of our tests @property (nonatomic, strong) GHVCollection *collection; @end !@implementation GHVRepoCollectionTests // Set up the `repos` collection for each test - (void)setUp { [super setUp]; self.collection = [GHVCollection new]; } // Add a test - (void)testAddingARepo { // Add a repo [self.collection addRepo:[GHVRepo new]]; ! // Assert the number of repos is now one XCTAssertEqual([self.collection.repos count], 1, @"Expected one repository"); } @end
Example of iOS TDD Using XCTest
![Page 14: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/14.jpg)
// Create an XCTestCase subclass @interface GHVRepoCollectionTests : XCTestCase // The subject of our tests @property (nonatomic, strong) GHVCollection *collection; @end !@implementation GHVRepoCollectionTests // Set up the `repos` collection for each test - (void)setUp { [super setUp]; self.collection = [GHVCollection new]; } // Add a test - (void)testAddingARepo { // Add a repo [self.collection addRepo:[GHVRepo new]]; ! // Assert the number of repos is now one XCTAssertEqual([self.collection.repos count], 1, @"Expected one repository"); } @end
Example of iOS TDD Using XCTest
![Page 15: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/15.jpg)
// Create an XCTestCase subclass @interface GHVRepoCollectionTests : XCTestCase // The subject of our tests @property (nonatomic, strong) GHVCollection *collection; @end !@implementation GHVRepoCollectionTests // Set up the `repos` collection for each test - (void)setUp { [super setUp]; self.collection = [GHVCollection new]; } // Add a test - (void)testAddingARepo { // Add a repo [self.collection addRepo:[GHVRepo new]]; ! // Assert the number of repos is now one XCTAssertEqual([self.collection.repos count], 1, @"Expected one repository"); } @end
Example of iOS TDD Using XCTest
![Page 16: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/16.jpg)
// Create an XCTestCase subclass @interface GHVRepoCollectionTests : XCTestCase // The subject of our tests @property (nonatomic, strong) GHVCollection *collection; @end !@implementation GHVRepoCollectionTests // Set up the `repos` collection for each test - (void)setUp { [super setUp]; self.collection = [GHVCollection new]; } // Add a test - (void)testAddingARepo { // Add a repo [self.collection addRepo:[GHVRepo new]]; ! // Assert the number of repos is now one XCTAssertEqual([self.collection.repos count], 1, @"Expected one repository"); } @end
Example of iOS TDD Using XCTest
![Page 17: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/17.jpg)
// Create an XCTestCase subclass @interface GHVRepoCollectionTests : XCTestCase // The subject of our tests @property (nonatomic, strong) GHVCollection *collection; @end !@implementation GHVRepoCollectionTests // Set up the `repos` collection for each test - (void)setUp { [super setUp]; self.collection = [GHVCollection new]; } // Add a test - (void)testAddingARepo { // Add a repo [self.collection addRepo:[GHVRepo new]]; ! // Assert the number of repos is now one XCTAssertEqual([self.collection.repos count], 1, @"Expected one repository"); } @end
Example of iOS TDD Using XCTest
![Page 18: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/18.jpg)
// Create an XCTestCase subclass @interface GHVRepoCollectionTests : XCTestCase // The subject of our tests @property (nonatomic, strong) GHVCollection *collection; @end !@implementation GHVRepoCollectionTests // Set up the `repos` collection for each test - (void)setUp { [super setUp]; self.collection = [GHVCollection new]; } // Add a test - (void)testAddingARepo { // Add a repo [self.collection addRepo:[GHVRepo new]]; ! // Assert the number of repos is now one XCTAssertEqual([self.collection.repos count], 1, @"Expected one repository"); } @end
Example of iOS TDD Using XCTest
![Page 19: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/19.jpg)
Behavior-Driven Development
• Answers the question: “What do I test?” • Behavioral tests don’t test the implementation, they specify the behavior
![Page 20: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/20.jpg)
iOS BDD Using Kiwi
![Page 21: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/21.jpg)
// Describe how the `GHVCollection` class behaves describe(@"GHVCollection", ^{ // Setup up the collection for each test __block GHVCollection *collection = nil; beforeEach(^{ collection = [GHVCollection new]; }); ! // Describe how the `-addRepo:` method behaves describe(@"-addRepo:", ^{ context(@"after adding a repo", ^{ // Add a repo before each test beforeEach(^{ [collection addRepo:[GHVRepo new]]; }); // Test the method behaves correctly it(@"has a repo count of one", ^{ [[collection.repos should] haveCountOf:1]; }); }); }); });
iOS BDD Using Kiwi
![Page 22: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/22.jpg)
// Describe how the `GHVCollection` class behaves describe(@"GHVCollection", ^{ // Setup up the collection for each test __block GHVCollection *collection = nil; beforeEach(^{ collection = [GHVCollection new]; }); ! // Describe how the `-addRepo:` method behaves describe(@"-addRepo:", ^{ context(@"after adding a repo", ^{ // Add a repo before each test beforeEach(^{ [collection addRepo:[GHVRepo new]]; }); // Test the method behaves correctly it(@"has a repo count of one", ^{ [[collection.repos should] haveCountOf:1]; }); }); }); });
iOS BDD Using Kiwi
![Page 23: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/23.jpg)
// Describe how the `GHVCollection` class behaves describe(@"GHVCollection", ^{ // Setup up the collection for each test __block GHVCollection *collection = nil; beforeEach(^{ collection = [GHVCollection new]; }); ! // Describe how the `-addRepo:` method behaves describe(@"-addRepo:", ^{ context(@"after adding a repo", ^{ // Add a repo before each test beforeEach(^{ [collection addRepo:[GHVRepo new]]; }); // Test the method behaves correctly it(@"has a repo count of one", ^{ [[collection.repos should] haveCountOf:1]; }); }); }); });
iOS BDD Using Kiwi
![Page 24: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/24.jpg)
// Describe how the `GHVCollection` class behaves describe(@"GHVCollection", ^{ // Setup up the collection for each test __block GHVCollection *collection = nil; beforeEach(^{ collection = [GHVCollection new]; }); ! // Describe how the `-addRepo:` method behaves describe(@"-addRepo:", ^{ context(@"after adding a repo", ^{ // Add a repo before each test beforeEach(^{ [collection addRepo:[GHVRepo new]]; }); // Test the method behaves correctly it(@"has a repo count of one", ^{ [[collection.repos should] haveCountOf:1]; }); }); }); });
iOS BDD Using Kiwi
![Page 25: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/25.jpg)
// Describe how the `GHVCollection` class behaves describe(@"GHVCollection", ^{ // Setup up the collection for each test __block GHVCollection *collection = nil; beforeEach(^{ collection = [GHVCollection new]; }); ! // Describe how the `-addRepo:` method behaves describe(@"-addRepo:", ^{ context(@"after adding a repo", ^{ // Add a repo before each test beforeEach(^{ [collection addRepo:[GHVRepo new]]; }); // Test the method behaves correctly it(@"has a repo count of one", ^{ [[collection.repos should] haveCountOf:1]; }); }); }); });
iOS BDD Using Kiwi
![Page 26: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/26.jpg)
// Describe how the `GHVCollection` class behaves describe(@"GHVCollection", ^{ // Setup up the collection for each test __block GHVCollection *collection = nil; beforeEach(^{ collection = [GHVCollection new]; }); ! // Describe how the `-addRepo:` method behaves describe(@"-addRepo:", ^{ context(@"after adding a repo", ^{ // Add a repo before each test beforeEach(^{ [collection addRepo:[GHVRepo new]]; }); // Test the method behaves correctly it(@"has a repo count of one", ^{ [[collection.repos should] haveCountOf:1]; }); }); }); });
iOS BDD Using Kiwi
![Page 27: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/27.jpg)
Kiwi Benefits
![Page 28: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/28.jpg)
Kiwi Benefits
• An unlimited amount of setup and teardown
![Page 29: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/29.jpg)
Kiwi Benefits
• An unlimited amount of setup and teardownbeforeEach(^{ /* ... */ }); beforeAll(^{ /* ... */ }); afterEach(^{ /* ... */ }); afterAll(^{ /* ... */ });
![Page 30: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/30.jpg)
Kiwi Benefits
• An unlimited amount of setup and teardownbeforeEach(^{ /* ... */ }); beforeAll(^{ /* ... */ }); afterEach(^{ /* ... */ }); afterAll(^{ /* ... */ });
• Mocks and stubs included
![Page 31: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/31.jpg)
Kiwi Benefits
• An unlimited amount of setup and teardownbeforeEach(^{ /* ... */ }); beforeAll(^{ /* ... */ }); afterEach(^{ /* ... */ }); afterAll(^{ /* ... */ });
• Mocks and stubs included[collection stub:@selector(addRepo:)];
![Page 32: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/32.jpg)
Kiwi Benefits
• An unlimited amount of setup and teardownbeforeEach(^{ /* ... */ }); beforeAll(^{ /* ... */ }); afterEach(^{ /* ... */ }); afterAll(^{ /* ... */ });
• Mocks and stubs included[collection stub:@selector(addRepo:)];
• Asynchronous testing support
![Page 33: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/33.jpg)
Kiwi Benefits
• An unlimited amount of setup and teardownbeforeEach(^{ /* ... */ }); beforeAll(^{ /* ... */ }); afterEach(^{ /* ... */ }); afterAll(^{ /* ... */ });
• Mocks and stubs included[collection stub:@selector(addRepo:)];
• Asynchronous testing support[[collection.repos shouldEventually] haveCountOf:2];
![Page 34: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/34.jpg)
Kiwi Benefits
• An unlimited amount of setup and teardownbeforeEach(^{ /* ... */ }); beforeAll(^{ /* ... */ }); afterEach(^{ /* ... */ }); afterAll(^{ /* ... */ });
• Mocks and stubs included[collection stub:@selector(addRepo:)];
• Asynchronous testing support[[collection.repos shouldEventually] haveCountOf:2];
• More readable than XCTest
![Page 35: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/35.jpg)
Our First Failing Test
![Page 36: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/36.jpg)
/// GHVAPIClientSpec.m !it(@"gets repositories", ^{ // The repos returned by the API __block NSArray *allRepos = nil; ! // Fetch the repos from the API [client allRepositoriesForUsername:@"modocache" success:^(NSArray *repos) { // Set the repos allRepos = repos; } failure:nil]; ! // Assert that the repos have been set [[expectFutureValue(allRepos) shouldEventually] haveCountOf:10]; });
Our First Failing Test
![Page 37: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/37.jpg)
/// GHVAPIClientSpec.m !it(@"gets repositories", ^{ // The repos returned by the API __block NSArray *allRepos = nil; ! // Fetch the repos from the API [client allRepositoriesForUsername:@"modocache" success:^(NSArray *repos) { // Set the repos allRepos = repos; } failure:nil]; ! // Assert that the repos have been set [[expectFutureValue(allRepos) shouldEventually] haveCountOf:10]; });
Our First Failing Test
![Page 38: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/38.jpg)
/// GHVAPIClientSpec.m !it(@"gets repositories", ^{ // The repos returned by the API __block NSArray *allRepos = nil; ! // Fetch the repos from the API [client allRepositoriesForUsername:@"modocache" success:^(NSArray *repos) { // Set the repos allRepos = repos; } failure:nil]; ! // Assert that the repos have been set [[expectFutureValue(allRepos) shouldEventually] haveCountOf:10]; });
Our First Failing Test
![Page 39: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/39.jpg)
/// GHVAPIClientSpec.m !it(@"gets repositories", ^{ // The repos returned by the API __block NSArray *allRepos = nil; ! // Fetch the repos from the API [client allRepositoriesForUsername:@"modocache" success:^(NSArray *repos) { // Set the repos allRepos = repos; } failure:nil]; ! // Assert that the repos have been set [[expectFutureValue(allRepos) shouldEventually] haveCountOf:10]; });
Our First Failing Test
![Page 40: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/40.jpg)
/// GHVAPIClientSpec.m !it(@"gets repositories", ^{ // The repos returned by the API __block NSArray *allRepos = nil; ! // Fetch the repos from the API [client allRepositoriesForUsername:@"modocache" success:^(NSArray *repos) { // Set the repos allRepos = repos; } failure:nil]; ! // Assert that the repos have been set [[expectFutureValue(allRepos) shouldEventually] haveCountOf:10]; });
Our First Failing Test
![Page 41: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/41.jpg)
/// GHVAPIClientSpec.m !it(@"gets repositories", ^{ // The repos returned by the API __block NSArray *allRepos = nil; ! // Fetch the repos from the API [client allRepositoriesForUsername:@"modocache" success:^(NSArray *repos) { // Set the repos allRepos = repos; } failure:nil]; ! // Assert that the repos have been set [[expectFutureValue(allRepos) shouldEventually] haveCountOf:10]; });
Our First Failing Test
![Page 42: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/42.jpg)
Going Green
![Page 43: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/43.jpg)
Going Green
/// GHVAPIClient.m !// Create a request operation manager pointing at the GitHub API NSString *urlString = @"https://api.github.com/"; NSURL *baseURL = [NSURL URLWithString:urlString]; AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; !// The manager should serialize the response as JSON manager.requestSerializer = [AFJSONRequestSerializer serializer]; !// Send a request to GET /users/:username/repos [manager GET:[NSString stringWithFormat:@"users/%@/repos", username] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { // Send response object to success block success(responseObject); } failure:nil]; }
![Page 44: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/44.jpg)
Going Green
/// GHVAPIClient.m !// Create a request operation manager pointing at the GitHub API NSString *urlString = @"https://api.github.com/"; NSURL *baseURL = [NSURL URLWithString:urlString]; AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; !// The manager should serialize the response as JSON manager.requestSerializer = [AFJSONRequestSerializer serializer]; !// Send a request to GET /users/:username/repos [manager GET:[NSString stringWithFormat:@"users/%@/repos", username] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { // Send response object to success block success(responseObject); } failure:nil]; }
![Page 45: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/45.jpg)
Going Green
/// GHVAPIClient.m !// Create a request operation manager pointing at the GitHub API NSString *urlString = @"https://api.github.com/"; NSURL *baseURL = [NSURL URLWithString:urlString]; AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; !// The manager should serialize the response as JSON manager.requestSerializer = [AFJSONRequestSerializer serializer]; !// Send a request to GET /users/:username/repos [manager GET:[NSString stringWithFormat:@"users/%@/repos", username] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { // Send response object to success block success(responseObject); } failure:nil]; }
![Page 46: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/46.jpg)
Going Green
/// GHVAPIClient.m !// Create a request operation manager pointing at the GitHub API NSString *urlString = @"https://api.github.com/"; NSURL *baseURL = [NSURL URLWithString:urlString]; AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; !// The manager should serialize the response as JSON manager.requestSerializer = [AFJSONRequestSerializer serializer]; !// Send a request to GET /users/:username/repos [manager GET:[NSString stringWithFormat:@"users/%@/repos", username] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { // Send response object to success block success(responseObject); } failure:nil]; }
![Page 47: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/47.jpg)
Going Green
/// GHVAPIClient.m !// Create a request operation manager pointing at the GitHub API NSString *urlString = @"https://api.github.com/"; NSURL *baseURL = [NSURL URLWithString:urlString]; AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; !// The manager should serialize the response as JSON manager.requestSerializer = [AFJSONRequestSerializer serializer]; !// Send a request to GET /users/:username/repos [manager GET:[NSString stringWithFormat:@"users/%@/repos", username] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { // Send response object to success block success(responseObject); } failure:nil]; }
![Page 48: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/48.jpg)
Problems with our Test
• The test has external dependencies • It’ll fail if the GitHub API is down • It’ll fail if run without an internet connection • It’ll fail if the response is too slow
• The test is slow • It sends a request every time it’s run
![Page 49: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/49.jpg)
HTTP Stubbing
stubRequest(@"GET", @“https://api.github.com/" @“users/modocache/repos") .andReturn(200) .withHeaders(@{@"Content-Type": @"application/json"}) .withBody(@"[\"repo-1\"]"); !GHVAPIClient *client = [GHVAPIClient new]; !// ... ![[expectFutureValue(allRepos) shouldEventually] haveCountOf:1];
Eliminating external dependencies
![Page 50: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/50.jpg)
HTTP Stubbing
stubRequest(@"GET", @“https://api.github.com/" @“users/modocache/repos") .andReturn(200) .withHeaders(@{@"Content-Type": @"application/json"}) .withBody(@"[\"repo-1\"]"); !GHVAPIClient *client = [GHVAPIClient new]; !// ... ![[expectFutureValue(allRepos) shouldEventually] haveCountOf:1];
Eliminating external dependencies
![Page 51: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/51.jpg)
HTTP Stubbing
stubRequest(@"GET", @“https://api.github.com/" @“users/modocache/repos") .andReturn(200) .withHeaders(@{@"Content-Type": @"application/json"}) .withBody(@"[\"repo-1\"]"); !GHVAPIClient *client = [GHVAPIClient new]; !// ... ![[expectFutureValue(allRepos) shouldEventually] haveCountOf:1];
Eliminating external dependencies
![Page 52: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/52.jpg)
HTTP Stubbing
stubRequest(@"GET", @“https://api.github.com/" @“users/modocache/repos") .andReturn(200) .withHeaders(@{@"Content-Type": @"application/json"}) .withBody(@"[\"repo-1\"]"); !GHVAPIClient *client = [GHVAPIClient new]; !// ... ![[expectFutureValue(allRepos) shouldEventually] haveCountOf:1];
Eliminating external dependencies
![Page 53: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/53.jpg)
HTTP Stubbing
stubRequest(@"GET", @“https://api.github.com/" @“users/modocache/repos") .andReturn(200) .withHeaders(@{@"Content-Type": @"application/json"}) .withBody(@"[\"repo-1\"]"); !GHVAPIClient *client = [GHVAPIClient new]; !// ... ![[expectFutureValue(allRepos) shouldEventually] haveCountOf:1];
Eliminating external dependencies
![Page 54: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/54.jpg)
HTTP Stubbing
stubRequest(@"GET", @“https://api.github.com/" @“users/modocache/repos") .andReturn(200) .withHeaders(@{@"Content-Type": @"application/json"}) .withBody(@"[\"repo-1\"]"); !GHVAPIClient *client = [GHVAPIClient new]; !// ... ![[expectFutureValue(allRepos) shouldEventually] haveCountOf:1];
Eliminating external dependencies
![Page 55: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/55.jpg)
Problems Nocilla Fixes
• The test no longer has external dependencies • It’ll pass whether the GitHub API is online or not • It’ll pass even when run offline
• The test is fast • It still sends a request, but that request is immediately intercepted and a response is returned
![Page 56: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/56.jpg)
Other Nocilla Features
![Page 57: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/57.jpg)
Other Nocilla Features
• Stub HTTP requests using regular expressions
![Page 58: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/58.jpg)
Other Nocilla Features
• Stub HTTP requests using regular expressionsstubRequest(@"GET", @"https://api.github.com/" @"users/(.*?)/repos".regex)
![Page 59: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/59.jpg)
Other Nocilla Features
• Stub HTTP requests using regular expressionsstubRequest(@"GET", @"https://api.github.com/" @"users/(.*?)/repos".regex)
• Return errors, such as for poor internet connection
![Page 60: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/60.jpg)
Other Nocilla Features
• Stub HTTP requests using regular expressionsstubRequest(@"GET", @"https://api.github.com/" @"users/(.*?)/repos".regex)
• Return errors, such as for poor internet connectionNSError *error = [NSError errorWithDomain:NSURLErrorDomain code:29 userInfo:@{NSLocalizedDescriptionKey: @"Uh-oh!"}]; stubRequest(@"GET", @"...") .andFailWithError(error);
![Page 61: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/61.jpg)
Takeaways
![Page 62: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/62.jpg)
Takeaways
• Readable, behavior-driven, asynchronous tests with Kiwi • https://github.com/allending/Kiwi
![Page 63: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/63.jpg)
Takeaways
• Readable, behavior-driven, asynchronous tests with Kiwi • https://github.com/allending/Kiwi
pod "Kiwi/XCTest"
![Page 64: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/64.jpg)
Takeaways
• Readable, behavior-driven, asynchronous tests with Kiwi • https://github.com/allending/Kiwi
• Eliminate network dependencies with Nocilla • https://github.com/luisobo/Nocilla
pod "Kiwi/XCTest"
![Page 65: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/65.jpg)
Takeaways
• Readable, behavior-driven, asynchronous tests with Kiwi • https://github.com/allending/Kiwi
• Eliminate network dependencies with Nocilla • https://github.com/luisobo/Nocilla
pod "Kiwi/XCTest"
pod "Nocilla"
![Page 66: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/66.jpg)
Questions?@modocache #startup_ios
![Page 67: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/67.jpg)
describe(@"this talk", ^{ context(@"after presenting the slides", ^{ it(@"moves to Q&A", ^{ [[you should] askQuestions]; [[you shouldEventually] receive:@selector(stop)]; }); }); });
Questions?@modocache #startup_ios
![Page 68: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/68.jpg)
describe(@"this talk", ^{ context(@"after presenting the slides", ^{ it(@"moves to Q&A", ^{ [[you should] askQuestions]; [[you shouldEventually] receive:@selector(stop)]; }); }); });
Questions?@modocache #startup_ios
![Page 69: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/69.jpg)
describe(@"this talk", ^{ context(@"after presenting the slides", ^{ it(@"moves to Q&A", ^{ [[you should] askQuestions]; [[you shouldEventually] receive:@selector(stop)]; }); }); });
Questions?@modocache #startup_ios
![Page 70: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/70.jpg)
describe(@"this talk", ^{ context(@"after presenting the slides", ^{ it(@"moves to Q&A", ^{ [[you should] askQuestions]; [[you shouldEventually] receive:@selector(stop)]; }); }); });
Questions?@modocache #startup_ios
![Page 71: iOS Behavior-Driven Development](https://reader035.vdocument.in/reader035/viewer/2022081513/554a0397b4c905e56c8b536b/html5/thumbnails/71.jpg)
describe(@"this talk", ^{ context(@"after presenting the slides", ^{ it(@"moves to Q&A", ^{ [[you should] askQuestions]; [[you shouldEventually] receive:@selector(stop)]; }); }); });
Questions?@modocache #startup_ios