Глава 11: Лучшие практики и паттерны проектирования ← Вернуться к списку глав Глава 13: Работа с компонентами iOS SDK

Глава 12: Тестирование в Objective-C

Добро пожаловать в двенадцатую главу нашего увлекательного путешествия по Objective-C! В этой главе мы познакомимся с тестированием — важным этапом разработки приложений, который помогает обеспечить их качество и надежность. Вы узнаете, как писать и запускать тесты, использовать фреймворк XCTest, а также получите практические советы по организации процесса тестирования. Как всегда, мы объясним все простым и понятным языком, чтобы вы могли легко во всем разобраться.


12.1. Почему тестирование важно?

Качество и надежность приложения

Типы тестирования


12.2. Введение в XCTest

Что такое XCTest?

Почему использовать XCTest?


12.3. Создание тестового проекта

Шаг 1: Создание нового проекта

  1. Откройте Xcode и выберите "Create a new Xcode project".
  2. Выберите шаблон "Single View App" и нажмите "Next".
  3. Укажите имя проекта, например, "TestingDemo", и выберите Objective-C в качестве языка.
  4. Убедитесь, что флажок "Include Unit Tests" установлен.

Шаг 2: Структура проекта с тестами


12.4. Понимание структуры тестового класса

Шаблон тестового класса

#import <XCTest/XCTest.h>

@interface TestingDemoTests : XCTestCase

@end

@implementation TestingDemoTests

- (void)setUp {
    [super setUp];
    // Код, который выполняется перед запуском каждого теста
}

- (void)tearDown {
    // Код, который выполняется после завершения каждого теста
    [super tearDown];
}

- (void)testExample {
    // Пример тестового метода
    XCTAssert(YES, @"Проверка, что выражение истинно");
}

@end

Методы setUp и tearDown

Создание тестовых методов

Пример:

- (void)testAddition {
    // Ваш тестовый код
}

12.5. Написание простого теста

Шаг 1: Добавление метода в основной код

Добавим в наш проект класс Calculator с методом сложения.

Calculator.h

#import <Foundation/Foundation.h>

@interface Calculator : NSObject

- (NSInteger)addNumber:(NSInteger)a toNumber:(NSInteger)b;

@end

Calculator.m

#import "Calculator.h"

@implementation Calculator

- (NSInteger)addNumber:(NSInteger)a toNumber:(NSInteger)b {
    return a + b;
}

@end

Шаг 2: Импорт класса в тестовый файл

TestingDemoTests.m

#import <XCTest/XCTest.h>
#import "Calculator.h"

@interface TestingDemoTests : XCTestCase

@property (nonatomic, strong) Calculator *calculator;

@end

Шаг 3: Инициализация в setUp

@implementation TestingDemoTests

- (void)setUp {
    [super setUp];
    // Инициализируем объект калькулятора перед каждым тестом
    self.calculator = [[Calculator alloc] init];
}

- (void)tearDown {
    // Обнуляем объект калькулятора после каждого теста
    self.calculator = nil;
    [super tearDown];
}

Шаг 4: Написание тестового метода

- (void)testAddition {
    NSInteger result = [self.calculator addNumber:5 toNumber:7];
    XCTAssertEqual(result, 12, @"Ожидается, что 5 + 7 = 12");
}

12.6. Запуск и анализ тестов

Запуск тестов в Xcode

Анализ результатов

Пример проваленного теста

Если мы изменим ожидаемый результат в нашем тесте:

XCTAssertEqual(result, 10, @"Ожидается, что 5 + 7 = 10");

12.7. Другие виды утверждений (Assertions)

Основные утверждения

Примеры использования

- (void)testStringIsNotEmpty {
    NSString *string = @"Hello, World!";
    XCTAssertFalse([string isEqualToString:@""], @"Строка не должна быть пустой");
}

- (void)testArrayContainsObject {
    NSArray *array = @[@1, @2, @3];
    XCTAssertTrue([array containsObject:@2], @"Массив должен содержать число 2");
}

12.8. Тестирование исключений

Проверка выброса исключений

Пример:

- (void)testDivisionByZero {
    Calculator *calculator = [[Calculator alloc] init];
    XCTAssertThrows([calculator divideNumber:10 byNumber:0], @"Ожидается выброс исключения при делении на ноль");
}

Calculator.h

- (double)divideNumber:(double)a byNumber:(double)b;

Calculator.m

- (double)divideNumber:(double)a byNumber:(double)b {
    if (b == 0) {
        @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Деление на ноль" userInfo:nil];
    }
    return a / b;
}

12.9. Тестирование производительности

Измерение времени выполнения кода

Пример:

- (void)testPerformanceExample {
    [self measureBlock:^{
        // Код, время выполнения которого нужно измерить
        NSMutableArray *array = [NSMutableArray array];
        for (NSInteger i = 0; i < 100000; i++) {
            [array addObject:@(i)];
        }
    }];
}

12.10. Лучшие практики при написании тестов

Пишите независимые тесты

Используйте setUp и tearDown

Давайте понятные имена тестовым методам

Проверяйте один сценарий на тест

Регулярно запускайте тесты


12.11. Организация тестов в больших проектах

Создание отдельных файлов для разных классов

Пример структуры:

Использование фикстур (Fixtures)

Пример:

- (User *)testUser {
    User *user = [[User alloc] init];
    user.name = @"Тестовый пользователь";
    user.age = 25;
    return user;
}

Моки и стабы (Mocks and Stubs)


12.12. Практические задания

Задание 1: Тестирование метода на четность

  1. Добавьте в класс Calculator метод - (BOOL)isEvenNumber:(NSInteger)number, который возвращает YES, если число четное.
  2. Напишите тестовый метод testIsEvenNumber для проверки этого метода с разными входными значениями.

Решение:

Calculator.m

- (BOOL)isEvenNumber:(NSInteger)number {
    return number % 2 == 0;
}

TestingDemoTests.m

- (void)testIsEvenNumber {
    XCTAssertTrue([self.calculator isEvenNumber:4], @"4 должно быть четным числом");
    XCTAssertFalse([self.calculator isEvenNumber:7], @"7 должно быть нечетным числом");
}

Задание 2: Тестирование обработки ошибок

  1. Добавьте в класс UserManager метод - (BOOL)loginWithUsername:(NSString *)username password:(NSString *)password error:(NSError **)error, который проверяет корректность учетных данных.
  2. Если имя пользователя или пароль пустые, метод должен возвращать NO и заполнять NSError.
  3. Напишите тесты для проверки корректной обработки ошибок.

Решение:

UserManager.h

@interface UserManager : NSObject

- (BOOL)loginWithUsername:(NSString *)username password:(NSString *)password error:(NSError **)error;

@end

UserManager.m

- (BOOL)loginWithUsername:(NSString *)username password:(NSString *)password error:(NSError **)error {
    if (username.length == 0 || password.length == 0) {
        if (error) {
            *error = [NSError errorWithDomain:@"UserManagerError" code:1001 userInfo:@{NSLocalizedDescriptionKey: @"Имя пользователя и пароль не могут быть пустыми"}];
        }
        return NO;
    }
    // Логика проверки учетных данных
    return YES;
}

UserManagerTests.m

- (void)testLoginWithEmptyUsername {
    UserManager *userManager = [[UserManager alloc] init];
    NSError *error = nil;
    BOOL success = [userManager loginWithUsername:@"" password:@"password123" error:&error];
    XCTAssertFalse(success, @"Ожидается, что вход не будет успешным");
    XCTAssertNotNil(error, @"Должна быть возвращена ошибка");
    XCTAssertEqual(error.code, 1001, @"Код ошибки должен быть 1001");
}

- (void)testLoginWithValidCredentials {
    UserManager *userManager = [[UserManager alloc] init];
    NSError *error = nil;
    BOOL success = [userManager loginWithUsername:@"user" password:@"password123" error:&error];
    XCTAssertTrue(success, @"Ожидается, что вход будет успешным");
    XCTAssertNil(error, @"Ошибка не должна быть возвращена");
}

Задание 3: Тестирование производительности сортировки

  1. Создайте массив из 10000 случайных чисел.
  2. Напишите тест производительности, который измеряет время сортировки массива.
  3. Используйте measureBlock: для измерения времени выполнения.

Решение:

TestingDemoTests.m

- (void)testSortingPerformance {
    // Генерация массива случайных чисел
    NSMutableArray *randomNumbers = [NSMutableArray array];
    for (NSInteger i = 0; i < 10000; i++) {
        [randomNumbers addObject:@(arc4random_uniform(100000))];
    }
    
    [self measureBlock:^{
        // Код сортировки
        NSArray *sortedArray = [randomNumbers sortedArrayUsingSelector:@selector(compare:)];
        XCTAssertNotNil(sortedArray, @"Результат сортировки не должен быть nil");
    }];
}

12.13. Заключение

Отличная работа! Вы познакомились с основами тестирования в Objective-C, узнали, как использовать фреймворк XCTest для написания и запуска тестов. Тестирование — неотъемлемая часть процесса разработки, и умение писать хорошие тесты делает вас более профессиональным разработчиком.


Что дальше?

Мы подошли к концу нашего путешествия по основам Objective-C. Теперь вы обладаете базовыми знаниями и навыками для разработки приложений на этом языке. Не останавливайтесь на достигнутом — продолжайте изучать новые технологии, углубляйте свои знания и практикуйтесь.

Не забывайте:

Удачи в ваших будущих проектах, и пусть ваше кодирование будет успешным и приятным!

Глава 11: Лучшие практики и паттерны проектирования ← Вернуться к списку глав Глава 13: Работа с компонентами iOS SDK

Просмотров: 45