Как стать автором
Обновить

Упрощение тестирования с Approval Tests в Dart

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров744

Approval Tests представляют собой альтернативный подход к традиционным утверждениям при тестировании программного обеспечения. Они особенно полезны при работе со сложными объектами, такими как длинные строки, коллекции или объекты с большим количеством свойств. Захватывая результат вывода и сравнивая его с утвержденной версией, утверждающие тесты упрощают процесс проверки того, что ваш код ведет себя так, как ожидается. В этой статье мы познакомимся с Dart-реализацией Approval Tests и продемонстрируем их использование на примере.

Что такое Approval Testing?

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

expect(a, equals(b));

В отличие от них, тесты утверждений используют подход проверки:

Approvals.verify(a);

Вот как это работает:

  1. Если результаты полностью совпадают с утвержденным файлом, тест пройден.

  2. Если есть разница, инструмент reporter подчеркивает несоответствие, и тест не проходит, предоставляя визуальное и текстовое представление изменений.

Running Approvals
Running Approvals

Если вы исправляете ошибку (или добавляете функцию), это изменит ожидаемое поведение. Поэтому при запуске теста он будет провален.

Add Behavior to Existing Approval
Add Behavior to Existing Approval

Зачем использовать Approval Tests?

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

Dart реализация для Approval Tests

Чтобы использовать Approval Tests в своем проекте Dart, добавьте следующее в файл pubspec.yaml:

dependencies:
  approval_tests: ^1.0.0

Начало работы

Установите и откройте стартовый проект: Approvaltests.Dart.StarterProject

Этот проект включает:

  • Файл .gitignore для исключения артефактов одобрения.

  • Линтер со всеми установленными правилами.

  • GitHub Actions для запуска тестов, при этом статус теста отображается на бейдже README.md.

Чтобы использовать Approval Tests:

  1. Настройте тест: Импортируйте библиотеку Approval Tests в свой код.

  2. Настройте репортер (по желанию): Репортеры выделяют различия между утвержденными и полученными файлами, когда тест проваливается. По умолчанию используется репортер CommandLineReporter. Вы также можете использовать DiffReporter для сравнения файлов в вашей IDE. В настоящее время пакет поддерживает VS Code и Android Studio. Если по какой-то ошибке или другой причине вы хотите создать свой собственный, есть опция customDiffInfo.

  3. Управление файлом «approved»: Когда тест запускается в первый раз, автоматически создается файл одобрения. Этот файл представляет собой ожидаемый результат. Как только результаты теста станут удовлетворительными, обновите утвержденный файл, чтобы отразить эти изменения.

Утверждение результатов

Утверждение результатов заключается в сохранении файла .approved.txt с желаемыми результатами. Распространенные подходы включают:

  • Использование diff tools для перемещения текста слева направо и сохранения результата.

  • Использование свойства approveResult в Options для автоматического утверждения после выполнения теста.

  • Переименование файла .received в .approved.

Репортеры

Репортеры запускают инструменты diff, когда что-то не совпадает, что позволяет легко увидеть изменения. Доступные репортеры включают:

  • CommandLineReporter: Выводит diff в терминал.

CommandLineReporter
CommandLineReporter
  • DiffReporter: Открывает инструмент Diff Tool в вашей IDE.

VS Code DiffReporter
VS Code DiffReporter

Пример: Ката «Gilded Rose»

Следующий пример демонстрирует, как использовать Approval Tests для рефакторинга кода GildedRose из Gilded Rose Kata:

final class GildedRose {
  final List<Item> items;

  GildedRose({required this.items});

  void updateQuality() {
    for (int i = 0; i < items.length; i++) {
      if (items[i].name != "Aged Brie" &&
          items[i].name != "Backstage passes to a TAFKAL80ETC concert") {
        if (items[i].quality > 0) {
          if (items[i].name != "Sulfuras, Hand of Ragnaros") {
            items[i].quality = items[i].quality - 1;
          }
        }
      } else {
        if (items[i].quality < 50) {
          items[i].quality = items[i].quality + 1;
          if (items[i].name == "Backstage passes to a TAFKAL80ETC concert") {
            if (items[i].sellIn < 11) {
              if (items[i].quality < 50) {
                items[i].quality = items[i].quality + 1;
              }
            }
            if (items[i].sellIn < 6) {
              if (items[i].quality < 50) {
                items[i].quality = items[i].quality + 1;
              }
            }
          }
        }
      }
      if (items[i].name != "Sulfuras, Hand of Ragnaros") {
        items[i].sellIn = items[i].sellIn - 1;
      }
      if (items[i].sellIn < 0) {
        if (items[i].name != "Aged Brie") {
          if (items[i].name != "Backstage passes to a TAFKAL80ETC concert") {
            if (items[i].quality > 0) {
              if (items[i].name != "Sulfuras, Hand of Ragnaros") {
                items[i].quality = items[i].quality - 1;
              }
            }
          } else {
            items[i].quality = items[i].quality - items[i].quality;
          }
        } else {
          if (items[i].quality < 50) {
            items[i].quality = items[i].quality + 1;
          }
        }
      }
    }
  }
}

final class Item {
  final String name;
  int sellIn;
  int quality;

  Item(this.name, {required this.sellIn, required this.quality});

  @override
  String toString() => 'Item{name: $name, sellIn: $sellIn, quality: $quality}';
}

Код теста:

import 'package:approval_tests/approval_tests.dart';
import 'package:starter_project/starter_project.dart';
import 'package:test/test.dart';

void main() {
  // Определяем все случаи
  const allTestCases = [
    [
      "foo",
      "Aged Brie",
      "Backstage passes to a TAFKAL80ETC concert",
      "Sulfuras, Hand of Ragnaros",
    ],
    [-1, 0, 5, 6, 10, 11],
    [-1, 0, 1, 49, 50],
  ];

  group('Approval Tests for Gilded Rose', () {
    test('verify all combinations', () {
      Approvals.verifyAllCombinations(
        allTestCases,
        // options: const Options(
        //   reporter: DiffReporter(),
        // ),
        processor: processItemCombination,
      );
    });
  });
}

// Функция для обработки каждой комбинации и создания вывода для проверки
String processItemCombination(Iterable<List<dynamic>> combinations) {
  final receivedBuffer = StringBuffer();

  for (final combination in combinations) {

    final String itemName = combination[0] as String;
    final int sellIn = combination[1] as int;
    final int quality = combination[2] as int;

    // Создаем объект Item, представляющий текущую комбинацию
    final Item testItem = Item(itemName, sellIn: sellIn, quality: quality);

    final GildedRose app = GildedRose(items: [testItem]);

    // Изменение качества testItem
    app.updateQuality();

    // Добавление обновленного элемента в ожидаемые Items
    receivedBuffer.writeln(testItem.toString());
  }

  // Возвращаем строковое представление обновленного элемента
  return receivedBuffer.toString();
}

И на выходе мы получаем 120 различных комбинаций, на основе которых можно приступать к рефакторингу кода. Конечно, можно разделить генерацию комбинаций на части и убрать ненужные, но это лишь пример.

120 комбинаций для ката «Позолоченная роза»
120 комбинаций для Gilded Rose Kata

Заключение

Approval Tests для Dart - это отличная альтернатива традиционным утверждениям, особенно для сложных объектов и выводов. Упрощая процесс проверки, они помогают разработчикам сосредоточиться на том, что изменилось, сокращая время и усилия, необходимые для понимания влияния изменений в коде. Дополнительную информацию, примеры и способы участия можно найти в репозитории Approval Tests Dart.

Прошу обратить внимание, что это моя первая статья и первая публичная библиотека. Я активно общаюсь с создателем Approval Tests Ллевелином Фалько по поводу улучшений библиотеки. Ваши предложения, проблемы и запросы на исправление очень приветствуются. Давайте вместе сделаем эту библиотеку еще лучше! 🙌

Поставьте звезду на репозиторий, чтобы поддержать проект! 🫰

По всем вопросам обращайтесь через Telegram или по электронной почте yelaman.yelmuratov@gmail.com.

Теги:
Хабы:
Всего голосов 3: ↑2 и ↓1+1
Комментарии0

Публикации

Истории

Работа

Ближайшие события

12 – 13 июля
Геймтон DatsDefense
Онлайн
19 сентября
CDI Conf 2024
Москва