【问题标题】:Mocking method with callback param带有回调参数的模拟方法
【发布时间】:2014-02-09 09:18:04
【问题描述】:

我正在尝试模拟这种方法:

$transformer = $this->transformerFactory->createProductTransformer($product, function (ProductInterface $product) use ($discount) {
    $product->setDiscount($discount);
});

它接受回调参数作为第二个参数,我不知道如何模拟它。

我正在使用 Mockery,所以它看起来像这样:

$this->transformerFactoryMock
    ->shouldReceive('createProductTransformer')
    ->with(???) // here!

如果我将相同的回调传递给 with() 方法,则实例不匹配。 如果 Mockery 不支持,我不介意使用 PHPUnit 模拟。

【问题讨论】:

    标签: php unit-testing mocking phpunit mockery


    【解决方案1】:

    如果您需要准确检查是否调用了回调,那么这是不可能的,因为每个 \Closure 实例都是唯一的,并且在您测试的方法中您创建了新的实例。

    但你可以做下一步:

    1) 验证类型的动态参数

    // Assume in test case you have these mock/stub
    $discount;
    $product;
    
    $transformerFactoryMock = \Mockery::mock('TransformerFactory')
        ->shouldReceive('createProductTransformer')
        ->with($product, \Mockery::type(\Closure::class))
    

    2) 检查任何隐藏的参数(在测试方法中调用模拟方法之前创建)

    $transformerFactoryMock = \Mockery::mock('TransformerFactory')
        ->shouldReceive('createProductTransformer')
        ->with(function(...$args) use ($product) {
            // check any args
        })
    

    3) 最后,使用传递给模拟方法的回调来伪造结果

    // then mock will be
    $transformerFactoryMock = \Mockery::mock('TransformerFactory')
        ->shouldReceive('createProductTransformer')
        ->andReturnUsing(function($closure) {
            // execute closure
            $closure();
    
            // any logic to fake return
            return;
        })
    

    查看mockery complex argument matchingdeclaring return value的文档

    【讨论】:

      【解决方案2】:

      如果“相同的回调”意味着相同的代码,那么它不是 PHP 的相同回调,因此 Mockery 不会接受它。

      var_dump(function () {} === function () {}); // false
      $func = function () {};
      var_dump($func === $func); // true
      

      要检查回调类型,您可以使用 mockery::type 方法(带有参数'closure'),更精确的检查是 mockery::on。 https://github.com/padraic/mockery#argument-validation

      【讨论】:

        【解决方案3】:

        很难从您粘贴的 sn-p 中准确判断您要在测试文件中做什么,但您可以模拟闭包及其参数。这是一个快速/肮脏/未经测试的示例,可以最好地猜测您要完成的工作:

        class Transformer {
        
            protected transformerFactory;
        
            public function __construct($factory) {
                $this->transformerFactory = $factor;
            }
        
            public function doSomething(Discount $discount, ProductInterface $product) {
        
                return $this->transformerFactory->createProductTransformer($product, function($product) use ($discount) {
                    $product->setDiscount($discount);
                });
            }
        
        }
        
        
        class TransformerTest {
        
            protected function makeTransformerWithFakeFactory()
            {
                $fakeFactory = \Mockery::mock('TransformerFactory');
        
                $transformer = new Transformer($fakeFactory);
        
                return array($tranformer, $fakeFactory);
            }
        
            protected function fakeDiscount()
            {
                return \Mockery::mock('Discount');
            }
        
            protected function fakeProduct()
            {
                return \Mocker::mock('ProductInterface');
            }
        
                    // first let's test to make sure that the factory's correct method is called with correct parameters
            function test_doSomething_WhenPassedProduct_CallsCreateProductTransformerOnFactoryWithProductAndClosure()
            {
                list($transformer, $mockFactory) = $this->makeTransformerWithFakeFactory();
                $fakeDiscount = $this->fakeDiscount();
                $fakeProduct = $this->fakeProduct();
        
                $mockFactory->shouldReceive('createProductTransformer')->once()->with($fakeProduct, \Mockery::type('Closure'));
        
                $transfomer->doSomething($fakeDiscount, $fakeProduct);
            }
        
                    // now, let's test to make sure that the $discount within the closure is called with the right method and params
                function test_doSomething_createProductTransformerCalledWithProductAndClosure_CallsSetDiscountOnProductWithDiscount()
            {
                list($transformer, $stubFactory) = $this->makeTransfomerWithFakeFactory();
                $fakeDiscount = $this->fakeDiscount();
                $mockProduct = $this->fakeProduct();
                $stubFactory->shouldReceive('createProductTransfomer')->passthru();
        
                $mockProduct->shouldReceive('setDiscount')->once()->with($fakeDiscount);
        
                $transfomer->doSomething($fakeDiscount, $mockProduct);
            }
        
                    // now lets make sure that doSomething returns what the call to factory's createProductTransformer method returns
            function test_doSomething_createProductTransformerCalledWithProductAndClosureReturnsValue_ReturnsSameValue()
            {
                list($transformer, $stubFactory) = $this->makeTransfomerWithFakeFactory();
                $fakeDiscount = $this->fakeDiscount();
                $fakeProduct = $this->fakeProduct();
                $stubFactory->shouldReceive('createProductTransfomer')->andReturn('transformed result');
        
                $result = $transfomer->doSomething($fakeDiscount, $fakeProduct);
        
                $this->assertEquals('transformed result', $result);
            }
        
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-10-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多