Как протестировать 2 метода с общей логикой?

Скажем, что у меня есть 2 общедоступных метода:

func didSelect(data: Data) {
    // do something

    self.view.showText(textForData(data))
}

func didDismiss(data: Data) {
    if data.isSomething {
        self.view.showText(textForData(data))
    }

    ...
}

private func textForData(data: Data): String {
    var text: String

    if data.distance == nil {
        text = "..."
    } else if data.distance < 1000 {
        text = "\(data.distance) m"
    } else {
        text = "\(data.distance / 1000) km"
    }

    return text
}

Оба они зависят от логики форматирования textForData.

textForData имеет (с этой минимизированной реализацией) 3 возможных случая. Если я проверю все возможные случаи для обеих моих публичных функций, у меня будет 6 методов тестирования, и 3 из них также будут тестировать ту же логику, которая уже была проверена другими 3.

Каков правильный способ тестирования этого?

Ps.: Я мог бы написать отдельный тест для textForData, а в тестах для общедоступных методов я утверждаю, что вызывается textForData, но, похоже, нарушает инкапсуляцию моего класса, и я не хочу чтобы сделать testForData общедоступным. Я также не хотел бы создавать отдельный класс только для моей логики textForData, потому что в конечном итоге создаю слишком много зависимостей для этого текущего класса, и эта логика, похоже, не подходит нигде, кроме этого класса.

Ответ 1

Здесь у вас есть несколько вариантов.

  • Не тестируйте textForData
  • Дублировать поведение в каждом тесте общедоступного метода, который использует его
  • Сделать textForData общедоступным
  • Сделать открытый класс для textForData

Точки 1 и 2 нежелательны.

Вы казались странно против пункта 3, но есть преимущества для этого. Вы сможете проверить это поведение один раз, учитывая, что вы действительно заботитесь об этом. Я не знаю Свифта, но на других языках это не так плохо, как кажется. Общий совет - это кодирование и интерфейс, а не реализация. Таким образом, открытый интерфейс этого класса будет иметь didSelect и didDismiss. Ваш производственный код будет выражаться в терминах этого интерфейса, что означает, что textForData является общедоступным методом в классе, к которому вы не можете получить доступ напрямую.

Хорошая новость заключается в том, что ваши тесты могут быть написаны против реализации (на самом деле, они должны), поэтому здесь у вас будет доступ ко всем трем методам. Таким образом, вы можете проверить свое содержание.

Точка 4 похожа на точку 3, но сохраняется как отдельный класс. Я бы выбрал это, учитывая, что вы можете утверждать, что мы нарушили принцип единой ответственности в пункте 3. Чтобы скрыть это, я бы ввел вложенный класс, чтобы начать с того, что вы указали, что этот код используется только в этом примере. Опять же, ваши тесты будут иметь доступ к этому, используя те же идеи, что и выше.

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

Ответ 2

Я считаю, что форматирование данных является собственной ответственностью. Поэтому вы должны извлечь его в свой класс.

Этот класс может быть проверен отдельно.

Ваши другие классы, которые используют этот, должны быть разделены с помощью интерфейса вместо класса напрямую. Зависимость должна быть введена (например, в constrcutor). Вы можете написать конструктор по умолчанию, создающий класс по умолчанию, чтобы упростить работу в производственном коде (плохая инъекция зависимости от мужчин).

Затем вы можете издеваться над форматировщиком и тестировать другой класс изолированно, проверяя, что метод textForData вызывается правильно.