【问题标题】:How can I mock the result of a method in my unit test?如何在单元测试中模拟方法的结果?
【发布时间】:2021-04-02 13:47:19
【问题描述】:

我正在学习 OOP 和简洁的编码实践,为了做到这一点,我开始在 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 文档和 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);
    }
}

我做错了什么?

【问题讨论】:

  • 看起来您正在正确地创建模拟,但您没有将模拟绑定到服务容器。相反,您手动创建一个新的GenericDice,而不是让服务容器解决它。

标签: php unit-testing oop mocking


【解决方案1】:

看起来您正在正确地创建模拟,但您没有将模拟绑定到服务容器。您正在手动创建一个新的GenericDice,而不是让服务容器解决它。创建 mock 并将其绑定到您的服务容器,然后让应用为您解析实例 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);
    }
}

【讨论】:

  • 谢谢!现在可以了!我肯定需要更多地了解 Laravel Container,因为它显然是我所知道的。
  • @RachidS 是的,即使在 Laravel 之外,它也是有用且良好的做法。
猜你喜欢
  • 2014-03-23
  • 1970-01-01
  • 2014-04-16
  • 1970-01-01
  • 2015-10-21
  • 2021-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多