Как я могу имитировать результат метода в моем модульном тестировании?

#php #unit-testing #oop #mocking

#php #модульное тестирование #ооп #издевательство

Вопрос:

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

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

 class GenericDice
{
    /**
     * 
     * @param int $min
     * @param int $max
     * @return int
     */
    public function random(int $min = 1, int $max = 100) : int
    {
        return mt_rand($min, $max);
    }
}
 

Теперь в моих тестах я хочу проверить, что при GenericDice::random() возврате 100 это означает, что я должен рассматривать действие как критический сбой.

Я прочитал документы Laravel Mocking docs, документы Mockery (в которых мне сказали не издеваться над статическими методами, поэтому я изменил способ использования GenericDice класса) и перепробовал много разных способов сделать это, но я не могу сказать, чтобы мои кости давали мне фиксированный результат.

Вот тест :

     public function test_character_should_lose_health_when_action_is_critical_failure()
    {
        $this->mock(GenericDice::class, function (MockInterface $mock) {
            $mock->shouldReceive("random")
                ->andReturn(100);
        });

        $character = Character::factory()->create([
            "current_health" => 50,
        ]);

        $response = $this->actingAs($character->user)->put(route("game.action.run"), [
            "ordre_id" => $this->cookAction->id,
        ]);

        $character->refresh();

        $this->assertLessThan(50, $character->current_health);
    }
 

Код, который запускает действие, выглядит следующим образом :

 class CookAction extends AbstractAction 
{
    public function run(): bool
    {
        // Let's roll baby...
        $this->diceResult = $this->throwDice();

        // How did I do?
        $this->checkForSuccess();

        // The cooking part below.
    }
}

// Below the AbstractAction
abstract class AbstractAction
{
    protected function throwDice(int $min = 1, int $max = 100): int
    {
        return (new GenericDice)->random($min, $max);
    }
}
 

Что я делаю не так?

Комментарии:

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

Ответ №1:

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

Попробуйте это

 public function test_character_should_lose_health_when_action_is_critical_failure()
{
    $diceMock = Mockery::mock(GenericDice::class)->makePartial();
    $diceMock->shouldReceive('random')->andReturn(100);

    // Bind to service container
     $this->app->bind(
        GenericDice::class,
        function () use ($diceMock) {
            return $diceMock;
        }
    );

    $character = Character::factory()->create([
        "current_health" => 50,
    ]);

    $response = $this->actingAs($character->user)->put(route("game.action.run"), [
        "ordre_id" => $this->cookAction->id,
    ]);

    $character->refresh();

    $this->assertLessThan(50, $character->current_health);
}
 

И это

 abstract class AbstractAction
{
    protected function throwDice(int $min = 1, int $max = 100): int
    {
          // Get the GenericeDice instance from the service container
          $dice = app(GenericDice::class);
          return $dice->random($min, $max);
    }
}
 

Комментарии:

1. Спасибо! Теперь это работает! Мне определенно нужно узнать больше о контейнере Laravel, поскольку в моих знаниях явно чего-то не хватает.

2. @RachidS Да, это полезная и хорошая практика даже за пределами Laravel.