Tag: XCTestExpectation

  • iOS Unit Test Examples / Snippets

    In this article I will post common iOS Unit testing examples for testing of:

    1. Asynchronous loading of data using REST API
    2. Loading of a UIView
    3. Method call after UITableViewCell is clicked
    4. Static UITableView sections have correct number of rows
    5. Loading of a UIView from UIStoryboard
    6. IBOutlet connection
    7. Model logic
    8. UITableViewCell subclass
    9. IBAction / Method is implemented
    10. Testing bundle name

    Asynchronous loading of data using REST API

    // Testing REST API GET /api/cities/ that returns an array of cities with id and name
    - (void)testCitiesAPI
    {
        // We create XCTestExpectation object to test asynchronous loading
        XCTestExpectation *testExpectation = [self expectationWithDescription:@"testCitiesAPI"];
        
        NSString *URLString = [NSString stringWithFormat:@"/api/cities/"];
        
        [[RSHTTPClient sharedClient] GET:URLString parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
            
            NSArray *jsonArray = (NSArray *)responseObject;
            
            // Checking that jsonArray is not empty
            XCTAssertNotNil(jsonArray);
            
            for (NSDictionary *dic in jsonArray) {
                RSCity *city = [RSCity objectWithProperties:dic];
                
                // Checking consistency of a model object created from JSON dic
                XCTAssertNotNil(city.name);
                XCTAssertNotEqual(city.sid, 0);
            }
            
            [testExpectation fulfill];
            
        } failure:^(NSURLSessionTask *task, NSError *error) {
            
            NSLog(@"Error: %@", error);
            XCTFail(@"Error in GET /api/cities/");
            
        }];
        
        // Waiting for 10 seconds untim fulfill is called when task is finished
        [self waitForExpectationsWithTimeout:10 handler:^(NSError *error) {
            
        }];
    }
    

    Here [RSHTTPClient sharedClient] is an instance of AFHTTPSessionManager subclass. But it doesn’t matter, the main is the principle.

    Loading of a UIView

    @interface RSBaseViewControllerTests : XCTestCase
    
    @property (nonatomic, strong) RSBaseViewController *vc;
    
    @end
    
    @implementation RSBaseViewControllerTests
    
    - (void)setUp {
        [super setUp];
        
        self.vc = [[RSBaseViewController alloc] init];
        [self.vc view];
    }
    
    - (void)tearDown {
        self.vc = nil;
        [super tearDown];
    }
    
    - (void)testThatViewLoads {
        XCTAssertNotNil(self.vc.view);
    }
    
    @end
    

    Method call after UITableViewCell is clicked

    #import "OCMock.h"
    
    # Mock objects for Objective-C
    pod 'OCMock', '~> 3.4'
    
    - (void)testThatHomekitRestoreCalled
    {
        id mock = [OCMockObject partialMockForObject:self.vc];
        
        [[mock expect] restoreHomeKitClicked];
        
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:CCSettingsSectionRestoreHomeKit];
        [self.vc tableView:self.vc.tableView didSelectRowAtIndexPath:indexPath];
        
        [mock verify];
    }
    

    Here we verify that method restoreHomeKitClicked is called when user selects a specific row in UITableView.

    Static UITableView sections have correct number of rows

    - (void)testThatSectionsHaveCorrentNumberOfRows
    {
        XCTAssertEqual([self.vc tableView:self.vc.tableView numberOfRowsInSection:CCSettingsSectionUpdate], 1);
        XCTAssertEqual([self.vc tableView:self.vc.tableView numberOfRowsInSection:CCSettingsSectionGeneral], 2);
        XCTAssertEqual([self.vc tableView:self.vc.tableView numberOfRowsInSection:CCSettingsSectionAdditional], 3);
        XCTAssertEqual([self.vc tableView:self.vc.tableView numberOfRowsInSection:CCSettingsSectionRestoreHomeKit], 1);
    }
    

    Loading of a UIView from UIStoryboard

    - (void)setUp
    {
        [super setUp];
        
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        self.vc = [storyboard instantiateViewControllerWithIdentifier:@"RECameraListViewController"];
        [self.vc performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES];
        [self.vc view];
    }
    
    
    - (void)tearDown
    {
        self.vc = nil;
        [super tearDown];
    }
    
    
    #pragma mark - View loading tests
    
    - (void)testThatViewLoads
    {
        XCTAssertNotNil(self.vc.view, @"View not initiated properly");
    }
    
    

    IBOutlet connection

    - (void)testOutlets {
        XCTAssertNotNil(self.vc.vkButton);
        XCTAssertNotNil(self.vc.fbButton);
        XCTAssertNotNil(self.vc.googleButton);
        XCTAssertNotNil(self.vc.agreementTextView);
    }
    

    Model logic

    - (void)testBalanceArrayCount
    {
        CSAccount *account = [CSAccount sharedAccount];
        NSArray *balanceArray = [account balanceArrayForNumberOfMonths:12];
        
        XCTAssertEqual([balanceArray count], 13);
    }
    

    Here we test that an array, that is formed inside our model has correct number of items. Model logic testing obviously can be different depending of logic itself.

    UITableViewCell subclass

    - (void)testThatServerCellHasCorrectOutlets
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
        REServerCell *cell = (REServerCell *)[self.vc tableView:self.vc.tableView cellForRowAtIndexPath:indexPath];
        XCTAssertNotNil(cell.cloudServerLabel, @"Should connect cloudServerLabel IBOutlet.");
        XCTAssertNotNil(cell.controlCenterServerLabel, @"Should connect controlCenterServerLabel IBOutlet.");
    }
    
    
    - (void)testThatServerCellHasCorrectContents
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
        REServerCell *cell = (REServerCell *)[self.vc tableView:self.vc.tableView cellForRowAtIndexPath:indexPath];
        
        REServerItem *item = self.vc.tableData[0];
        
        NSString *cloudServerURL = cell.cloudServerLabel.text;
        NSString *controlCenterServerURL = cell.controlCenterServerLabel.text;
        
        XCTAssertTrue([cloudServerURL isEqualToString:item.cloudServer]);
        XCTAssertTrue([controlCenterServerURL isEqualToString:item.controlCenterServer]);
    }
    

    IBAction / Method is implemented

    - (void)testThatHelperHasCustomizeAppearanceMethod
    {
        XCTAssertTrue([self.helper respondsToSelector:@selector(customizeAppearance)]);
    }
    

    Testing bundle name

    - (void)testThatBundleNameIsCorrect
    {
        NSString *correctBundleName = @"YourBundleName";
        NSDictionary *infoDic = [[NSBundle mainBundle] infoDictionary];
        NSString *bundleName = infoDic[@"CFBundleName"];
        XCTAssertTrue([bundleName isEqualToString:correctBundleName], @"Strings are not equal %@ %@", correctBundleName, bundleName);
    }
    

    The last is a rare case, but helps to prevent someone changing a bundle name of project accidentally.