【问题标题】:How to make Mockery overload option work properly on Laravel 5如何使 Mockery 重载选项在 Laravel 5 上正常工作
【发布时间】:2021-08-22 09:09:32
【问题描述】:

我正在尝试在 Laravel 5 上使用 Mockery 库的 overload 选项。

我现在的环境:

  • Laravel 5
  • 嘲讽 1.0
  • PHPUnit 7.5

我写了这个测试用例:

namespace Tests\Unit;

use Mockery;
use Tests\TestCase;

/**
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */
class RenewSignatureTest extends TestCase
{
    public function testHandle()
    {
        $mock = Mockery::mock('overload:App\FooClass');

        $mock->shouldReceive('callBar')
            ->times(2);
    }
}

根据documentation,这个测试应该失败,但不管我做什么,测试永远不会失败!它总是导致:

Time: 304 ms, Memory: 19.53 MB

OK (1 test, 1 assertion)

如果我删除 overload: 选项,测试将失败。所以我假设我没有按预期使用库的方法。

新测试:

namespace Tests\Unit;

use Mockery;
use Tests\TestCase;

/**
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */
class RenewSignatureTest extends TestCase
{
    public function testHandle()
    {
        $mock = Mockery::mock('App\FooClass');

        $mock->shouldReceive('callBar')
            ->times(2);
    }
}

结果:

Mockery\Exception\InvalidCountException: Method callBar(<Any Arguments>) from Mockery_0__App_FooClass should be called exactly 2 times but called 0 times.

我做错了吗?有谁知道如何正确使用这个选项?

【问题讨论】:

    标签: php laravel laravel-5 phpunit mockery


    【解决方案1】:

    阅读页面,我认为这是您正在寻找的错误:

    当 Mockery 重载一个类时,由于 PHP 处理文件的方式, 不能包含重载的类文件,否则 Mockery 将 抛出“类已存在”异常。这是自动加载的地方 开始工作,让我们的工作轻松很多。

    如果您从测试中删除这两行,则会导致您正在寻找的错误,它们被添加到第二个代码示例和手册页上的第三个示例中的代码中:

     * @runTestsInSeparateProcesses
     * @preserveGlobalState disabled
    

    这意味着您的代码不会利用 psr4 自动加载器或任何现有的自动加载器,并且会以牺牲速度为代价为您创建一个新的自动加载器,因为它不会使用您转储的类映射并且必须构建它从头开始。

    如果你去掉上面的两行,你会得到预期的错误,因为它会尝试用同名的类重载你的类。该类已经被自动加载,因此您会遇到致命错误。

    因此,如果您想阻止对 callBar 的调用并返回 void,这就是您的代码将执行的操作,这是正确的。

    删除overload 将意味着您的模拟不再有效,因为您必须将其传递给构造函数才能使其工作。

    更新:

    通过您的更新,我可以得出结论,您的代码必须运行两次模拟的 callBar 方法(不是实际的 callBar 方法),而您的模拟 fooBar 类使用 overload。当被模拟的方法被调用时,真正的callBar 方法内部实际上并没有发生任何事情,它只是注册它被调用了。例如,如果您期望它一次,请编写 shouldReceive(1),然后修复您的测试运行的代码。

    当您删除 overload 时,不会发生全局注入,因此您的模拟永远不会被注入。但是,您在模拟类上的模拟 callBar 方法仍期望运行两次,因此您会收到错误消息。您需要从测试中完全删除 2 个模拟代码行。

    保留 @ 语句,因为它有助于防止上述 psr4 错误。

    【讨论】:

    • 好的。我不知道我理解正确,但是,我的问题是我在两种情况下拥有完全相同的类:1-如果放置重载选项,则测试通过(这不应该发生,因为我明确表示 fooBar 需要被调用 2 次) 2-如果我删除重载选项,测试失败,说 Mockery_0__App_FooClass 应该被调用两次。为什么会发生这种情况?
    • PS:我知道我没有那么明确。对此我很抱歉。我更新了问题。
    • PS2:“为什么会发生这种情况”的意思是“为什么我在两个类中没有相同的结果?”
    • PS3:请记住我正在使用 Laravel 的 TestCase。 Wich 意味着存在隐式方法,例如框架实现的 setUp 和 tearDown 。如果你愿意,我可以在这里发布这些方法。
    • PS4:即使我删除那些“@ cmets”并单独运行此测试,我也会得到相同的结果。
    【解决方案2】:

    这不是测试的方式,你永远不应该使用overload: 选项...... Laravel 可以帮助你做很多事情,不要再次创建轮子或尝试......

    为什么需要使用@runTestsInSeparateProcesses@preserveGlobalState disabled


    假设你的测试是这样的:

    namespace Tests\Unit;
    
    use App\FooClass;
    use App\Service\ServiceForTesting;
    use Mockery;
    use Tests\TestCase;
    
    class RenewSignatureTest extends TestCase
    {
        public function testHandle()
        {
            $mock = Mockery::mock(FooClass::class);
    
            $mock->shouldReceive('callBar')
                ->times(2);
    
            $myClass = app(ServiceForTesting::class);
            $myClass->run($mock);
            $myClass->run($mock);
        }
    }
    

    你应该有这样的代码:

    namespace App;
    
    class FooClass
    {
        public function callBar()
        {
            return 'Works !!!';
        }
    }
    

    现在,假设你有一个独立的类(没有控制器或类似的,你在一个单元测试中),你应该有这样的东西:

    namespace App\Service;
    
    use App\FooClass;
    
    class ServiceForTesting
    {
        public function run(FooClass $class)
        {
            return $class->callBar();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2022-01-14
      • 2011-07-18
      • 1970-01-01
      • 2015-12-03
      • 2015-07-05
      • 2015-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多