Я очень часто листаю блог Сергея Теплякова, и считаю его одним из самых невероятных русскоязычных блогов, который я когда-либо читал в своей карьере программиста.

Сегодняшним вечером, после рабочего дня, я как обычно просматривал блог Сергея в поисках чего-то нового для себя и наткулся на потрясающую статью Параметризованные юнит тесты, в которой Сергей рассказал о том, что такой инструмент написания тестов незаслуженно многие обходят стороной.

После прочтения статьи, я был тоже удивлен, почему же я только сегодня узнал о таком потрясном и удобном инструменте. В ту же минуту я понял, что я бы непременно хотел использовать то же самое в iOS разработке. Но к сожалению Apple меня расстроили и Xcode не поддерживает данный вид тестов (после получаса гугления). Тем не менее, поиск не закончился неудачей! В процессе поиска я наткнулся на библиотеку KNMParametrizedTest, которая вполне себе выполняет поставленную задачу.

Если коротко:

  • Библиотека парсит написанный перед тестом макрос, в котором мы перечисляем название теста и параметры.
  • Затем парсит тесты с определенным названием и одним параметром в текущем XCTestCase
  • Из макроса перебирает все параметры, которые мы перечислили и выполняет Invocation теста ( который попадает под правила “параметризованный”) с переданным параметром.

В результате, у нас выполняется тест столько раз, сколько мы передали параметров в макросе.

Я сразу же решил попробовать интегрировать библиотеку в свой текущий проект и опробовать на своих же тестах и “О Боги!” - после рефакторинга некоторых тестов на теперь уже параметризированные, я был восхищен получившимся результатом.

Результат использования параметризованных юнит тестов

Что же, слов мало, давайте посмотрим на вариант тестов до рефакторинга. Я приведу вам пример тестов текст форматтера, и тесты, проверяющие поведение, когда мы вводим символы [1, 2, 7] и если это будет первый символ в текущем тексте TextField’а, то форматтер должен отсеивать такой символ и возвращать текущий текст из TextField’a.

- (void)testThatPhoneTextFormatterShouldReturnCurrentTextIfPhoneCodeIsStartFrom1 {
    // given
    NSString *currentText = @"";
    NSString *newString = @"1";

    // when
    NSString *formattedText = [self.phoneTextFormatter formatedStringFromString:newString currentText:currentText range:NSMakeRange(0, 0)];

    // then
    XCTAssertEqualObjects(formattedText, currentText);
}

- (void)testThatPhoneTextFormatterShouldReturnCurrentTextIfPhoneCodeIsStartFrom2 {
    // given
    NSString *currentText = @"";
    NSString *newString = @"2";

    // when
    NSString *formattedText = [self.phoneTextFormatter formatedStringFromString:newString currentText:currentText range:NSMakeRange(0, 0)];

    // then
    XCTAssertEqualObjects(formattedText, currentText);
}

- (void)testThatPhoneTextFormatterShouldReturnCurrentTextIfPhoneCodeIsStartFrom7 {
    // given
    NSString *currentText = @"";
    NSString *newString = @"7";

    // when
    NSString *formattedText = [self.phoneTextFormatter formatedStringFromString:newString currentText:currentText range:NSMakeRange(0, 0)];

    // then
    XCTAssertEqualObjects(formattedText, currentText);
}

Согласитесь, не самое разборчивое название тестов? Да и дублирование кода наблюдается.

Теперь давайте посморим как будет выглядеть такие тесты, а точнее уже тест после того как мы здесь используем параметризованный тест:

KNMParametersFor(
        test_whenFormattedStringFromString_thatTextFormatterShouldReturnCurrentText_withCurrentEmptyText_andWithTestValue,
        @[@"1", @"2", @"7"]
);
- (void)test_whenFormattedStringFromString_thatTextFormatterShouldReturnCurrentText_withCurrentEmptyText_andWithTestValue:(NSString *)testValue {
    // given
    NSString *currentText = @"";

    // when
    NSString *formattedText = [self.phoneTextFormatter formatedStringFromString:testValue currentText:currentText range:NSMakeRange(0, 0)];

    // then
    XCTAssertEqualObjects(formattedText, currentText, @"Test failed with test value: %@", testValue);
}

Теперь намного ведь круче и читаеме, верно? Такой же подход я советую теперь и вам, друзья. Это очень облегчит нам жизнь во мноих случаях.

Сергею большое спасибо, за то, что он раскрыл мне глаза на такую чудную вещь в своей статье.