#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.