Является ли использование тестовых фабрик в Laravel анти-образцом? - PullRequest
0 голосов
/ 25 декабря 2018

На этом слайде:

enter image description here

Сандиз Метц говорит о том, как мы должны проводить тестирование извне , ничего не зная о том, чтоидет в SUT.

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

Мне кажется, что использование этих фабрик означает, что я знаю, какими должны быть данные базы данных для SUT, чтобы выполнить свою задачу.

Например, скажем, пользовательдолжен иметь возможность редактировать свой профиль.С фабриками я мог бы сделать это:

/** @test */
public function the_user_can_update_his_profile()
{
    $user = factory(User::class)->create();

    // ACT

    // ASSERT
}

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

/** @test */
public function the_user_can_update_his_profile()
{
    $userRepository = app(UserRepository::class);
    $user = $userRepository->register('email@email.com', 'password123');

    // ACT

    // ASSERT
}

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

/** @test */
public function the_user_can_update_his_profile()
{
    $response = $this->json('POST', route('register_user'), ['email' => 'email@email.com', 'password' => 'password123']);
    $userRepository = app(UserRepository::class);
    $user = $userRepository->find($response['userId']);

    // ACT

    // ASSERT
}

Но это (экстремальное) решение может также сделать кучу ненужных других вещей (например, отправив электронное письмо с подтверждением).Для работы также необходим регистрационный маршрут.

Что вы считаете самым чистым решением для сложного проекта?

1 Ответ

0 голосов
/ 25 декабря 2018

Как указано в комментариях, вы ошибаетесь с целью и областью применения модульного теста и интеграционного теста , и отсюда возникает путаница.Начнем с вашего «теста»:

/** @test */
public function the_user_can_update_his_profile()
{
    $response = $this->json('POST', route('register_user'), ['email' => 'email@email.com', 'password' => 'password123']);
    $userRepository = app(UserRepository::class);
    $user = $userRepository->find($response['userId']);

    // ACT

    // ASSERT
}

Это интеграционный тест.Вы тестируете «удаленный» маршрут (хорошо, он внутренний, но вы все еще тестируете каждый отдельный компонент на своем пути).Интеграционный тест идеально подходит для подтверждения того, что бизнес-логика была реализована правильно и что маршрут ведет себя как ожидалось;он проходит свой путь от начала до завершения, тестируя каждое взаимодействие между компонентами и логикой в ​​контроллере + представление непосредственно.

Это все хорошо и хорошо, но на самом деле это нам не очень помогает.Мы все еще слепы в отношении тестируемости самих компонентов.Вот где начинается модульное тестирование.

Предположим, у вас есть класс следующим образом:

<?php
class Foo {
  public $value = 0;
  public function __construct($value) {
    $this->value = (int)$value;
  }
  public function getRemainder(int $item) {
    return $this->value % $item;
  }
}

Вы вполне можете использовать этот класс в одном из ваших контроллеров;смысл модульного тестирования состоит в том, чтобы утверждать, что:

  • конструктор сохраняет правильное значение (в виде целого числа)
  • метод isModulo делает то, что написано на банке.

Для этого мы можем написать следующий тест:

public function isActualModuloClass() {
  $modulo = new Foo(5);
  $modulo_float = new Foo(2.3);
  assert($modulo->value == 5, "Integer modulo constructor works");
  assert($modulo_float->value == 2, "Float modulo casts to integer properly");
  assert($modulo->getRemainder(5) == 0, "Modulo 5%5 is 0");
  assert($modulo->getRemainder(4) == 1, "Modulo 5%4 is 1");
}

И, конечно же, мы провериликаждый метод, каждая ветвь нашего компонента в изоляции .Это модульный тест.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...