@Tokenchik

Какой смысл mock объектов для юнит тестирования своего кода?

Всем привет, использую phpunit для тестов, столкнулся с непонимаем смысла мока классов и их методов.
Ситуация которая мне понятна - мок http клиента который возвращает конкрентный результат и завязан на внешний сервис, но как быть с простыми классами, которые не связаны с внешним миром?
Есть небольшой пример:
<?php
class SomeClass
{
    public function doSomething(): string
    {
        return 'foo';
    }
}

<?php
use PHPUnit\Framework\TestCase;

class StubTest extends TestCase
{
    public function testStub(): void
    {
        // Создать заглушку для класса SomeClass.
        $stub = $this->createMock(SomeClass::class);

        // Настроить заглушку.
        $stub->method('doSomething')
             ->willReturn('foo');

        // Вызов $stub->doSomething() теперь вернёт 'foo'.
        $this->assertSame('foo', $stub->doSomething());
    }
}


Какой смысл мока для таких классов? Можно ведь что угодно накидать в мок метода и в реальной жизни получать совсем не то, что проверяется во время такого теста.
  • Вопрос задан
  • 335 просмотров
Решения вопроса 3
@HellWalk
Смысл моков - эмулировать объекты с определенным поведением.

Самый банальный пример, помимо http запросов, это эмулирование неправильных объектов.

Допустим, у вас есть сервис, который обрабатывает какой-то объект. Объект написан хорошо, с валидацией данных, и его поведение корректное. Но чтобы вам написать качественный сервис - он не должен полагаться на то, что другой объект ведет себя корректно. Он должен дополнительно проверять граничные ситуации. И разумеется, на такие кейсы нужно написать тесты, а как их написать, если тестируемый объект написан так, что он ведет себя корректно? Вот здесь и приходят на помощь моки.

В phpunit есть функционал подсчета покрытия кода тестами - попробуйте на каком-нибудь относительно небольшом модуле добиться 100% покрытия кода тестами - вам обязательно придется использовать хитрые моки, эмулирующие нестандартное поведение объектов.

P.S. Если вы недавно знакомы с юнит-тестами - непонимание моков нормально. Если будете стремиться писать надежный код, с качественным покрытием кода тестами (здесь самое сложное - предугадать все плохие кейсы, которые будут пытаться сломать ваш код) - понимание придет.
Ответ написан
dmitriylanets
@dmitriylanets
веб-разработчик
Когда вы тестируете метод А вашего класса, вы проверяете его логику а не логику других (замоканых методов), может быть ситуация когда ваш метод А работает по логике корректно, а вот другой метод Б другого класса используемый в тестируемом методе выдает ошибку, возникает вопрос нужно ли считать что ваш метода А работает неправильно из за упавшего метода Б ?
Ответ написан
Отрывок из книги.
Одной из важных особенностей unit-тестирования является тестирование в
изоляции. Unit (класс, функция или другой модуль) должен быть изолирован
от всего остального мира.Это будет гарантировать, что тест тестирует только
этот модуль. Тест может упасть только по двум причинам: неправильный тест
или неправильный код тестируемого модуля. Тестирование в изоляции даёт
нам эту простоту и быстродействие

Обычно зависимость - это интерфейс, который имеет несколько реализа-
ций. Использование реальных реализаций этого интерфейса во время unit-
тестирования - плохая идея, поскольку там могут проводиться те самые опера-
ции ввода-вывода, замедляющие тестирование и не дающие провести тести-
рование этого модуля в изоляции. Прогон unit-тестов должен быть быстр как
молния, поскольку запускаться они будут часто и важно, чтобы разработчик
запустив их не потерял фокус над кодом. Написал код - прогнал тесты, еще
написал код - прогнал тесты. Быстрые тесты позволят ему оставаться более
продуктивным, не позволяя отвлекаться. Решение в лоб задачи изоляции
класса от зависимостей - создание отдельной реализации этого интерфейса,
предназначенного просто для тестирования.

Так вот суть в том, чтобы точечно проверить текущий класс на его функциональность. Моки удодно делать на основе общего интерфейса. Всё мокать не нужно. Но если ваш класс работает с апи, бд, большим обьемом данных. То такая зависимость это плохо.
Цель теста проверить текущий класс на соответствие требований. А какие данные настоящие или фейк это не важно.

К примеру есть класс Заказ. Нам нужно его тестировать. У него есть зависимости: Налог и прочее. Класс налога может быть класс, который получает Значение по api. Так вот есть смысл при тестировании Заказа создать мок Налога
$tax = $this->createMock(Tax::class);
$tax->method('calculateTax')
->willReturn(0);

$newOrder = new Order($tax);

Дальше идут тесты класс Order. Они будут изолированные. Есть основной класс, который ты тестируешь. Для него ты Мок не создаешь. А вот для классов с зависимостями создаются Моки, но не всегда. Если класс зависимостей например содержит просто константы. То создавать Мок и дублировать код смысла нет
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы