【问题标题】:phpunit testing method that calls other class methods which need mock调用其他需要模拟的类方法的phpunit测试方法
【发布时间】:2013-08-18 08:21:48
【问题描述】:

我正在尝试创建一个非常标准的单元测试,我在其中调用一个方法并断言它的响应,但是我正在测试的方法在同一个类中调用另一个方法,这会带来一些繁重的工作。

我想模拟一个方法,但仍按原样执行我正在测试的方法,仅使用从调用另一个方法返回的模拟值。

我已经简化了示例以使其尽可能简单。

class MyClass
{

    // I want to test this method, but mock the handleValue method to always return a set value.

    public function testMethod($arg)
    {

        $value = $arg->getValue();

        $this->handleValue($value);

    }


    // This method needs to be mocked to always return a set value.

    public function handleValue($value)
    {

        // Do a bunch of stuff...
        $value += 20;

        return $value;

    }

}

我尝试编写测试。

class MyClassTest extends \PHPUnit_Framework_TestCase
{


    public function testTheTestMethod()
    {

        // mock the object that is passed in as an arg
        $arg = $this->getMockBuilder('SomeEntity')->getMock();
        $arg->expects($this->any())
            ->method('getValue')
            ->will($this->returnValue(10));

        // test handle document()
        $myClass = new MyClass();

        $result = $myClass->testMethod($arg);

        // assert result is the correct
        $this->assertEquals($result, 50);

    }

}

我尝试过模拟 MyClass 对象,但是当我这样做并调用 testMethod 时,它总是返回 null。我需要一种方法来模拟一个方法,但保持对象的其余部分不变。

【问题讨论】:

    标签: php phpunit


    【解决方案1】:

    您可以模拟您正在测试的类并指定要模拟的方法。

    $mock = $this->getMockBuilder('MyClass')
        ->setMethods(array('handleValue'))
        ->getMock();
    
    $mock->expects($this->once())
        ->method('handleValue')
        ->will($this->returnValue(23)) //Whatever value you want to return
    

    但是,IMO 这不是您测试的最佳主意。这样的测试会使重构变得更加困难。您正在指定类的实现,而不是类应该具有的行为。如果handleValue 正在做很多复杂的工作,使测试变得困难,请考虑将逻辑移动到一个单独的类中并将其注入到您的类中。然后您可以创建该类的模拟并将其传递给testMethod。如果handleValue 需要调整其行为,这样做会给您带来额外的优势,使MyClass 更具可扩展性。

    http://www.oodesign.com/strategy-pattern.html

    作为一般规则,您不应模拟您正在测试的系统。

    【讨论】:

    • 如果方法handleValue在另一个类中怎么办?
    • 好答案,当我必须在我的测试方法和模拟方法之间使用部分模拟时(如果无法重构逻辑),我通常会应用测试依赖项
    • @Schleis:如果您只想检查某个函数是否被调用,这种方法会很有帮助。它帮助我检查了我的某个回归,因为没有调用某个函数。使用它可以使测试保持较小。
    【解决方案2】:

    您可以使用setMethods() 指定要模拟(部分模拟)的方法:

     // Let's do a `partial mock` of the object. By passing in an array of methods to `setMethods`
     // we are telling PHPUnit to only mock the methods we specify, in this case `handleValue()`.
    
    $csc = $this->getMockBuilder('Lightmaker\CloudSearchBundle\Controller\CloudSearchController')
                 ->setConstructorArgs($constructor)
                 ->setMethods(array('handleValue'))
                 ->getMock();
    
     // Tell the `handleValue` method to return 'bla'
     $csc->expects($this->any())
         ->method('handleValue')
         ->with('bla');
    

    你给setMethods() 的数组中未指定的类中的任何其他方法都将按原样执行。如果您使用setMethods,所有方法都将返回NULL,除非您专门设置它们。

    【讨论】:

      猜你喜欢
      • 2022-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-12
      • 1970-01-01
      • 2020-11-15
      • 1970-01-01
      相关资源
      最近更新 更多