【问题标题】:PHPUnit: How to mock a function on a class?PHPUnit:如何模拟类上的函数?
【发布时间】:2013-08-31 04:47:34
【问题描述】:

我有一个名为“QueryService”的类。在这个类中,有一个名为“GetErrorCode”的函数。在这个类上还有一个名为“DoQuery”的函数。所以你可以放心地说我有这样的东西:

class QueryService {
    function DoQuery($request) {
        $svc = new IntegratedService();
        $result = $svc->getResult($request);
        if ($result->success == false)
            $result->error = $this->GetErrorCode($result->errorCode);
    }

    function GetErrorCode($errorCode) {
         // do stuff
    }
}

我想创建一个 phpunit 测试来测试“DoQuery”。但是,我希望“GetErrorCode”的结果由模拟确定。换句话说,我想说如果 $errorCode = 1,GetErrorCode 必须绕过这个函数中的任何逻辑,只返回单词“ONE”。如果是 1 以外的任何数字,则必须返回“NO”。

如何使用 PHPUNIT Mocks 进行设置?

【问题讨论】:

    标签: php mocking phpunit


    【解决方案1】:

    要测试这个类,你可以模拟IntegratedService。然后,IntegratedService::getResult() 可以设置为在模拟中返回您喜欢的任何内容。

    然后测试变得更容易。您还需要能够使用依赖注入来传递模拟服务而不是真实服务。

    类:

    class QueryService {
        private $svc;
    
        // Constructor Injection, pass the IntegratedService object here
        public function __construct($Service = NULL)
        {
            if(! is_null($Service) )
            {
                if($Service instanceof IntegratedService)
                {
                    $this->SetIntegratedService($Service);
                }
            }
        }
    
        function SetIntegratedService(IntegratedService $Service)
        {
            $this->svc = $Service
        }
    
        function DoQuery($request) {
            $svc    = $this->svc;
            $result = $svc->getResult($request);
            if ($result->success == false)
                $result->error = $this->GetErrorCode($result->errorCode);
        }
    
        function GetErrorCode($errorCode) {
             // do stuff
        }
    }
    

    测试:

    class QueryServiceTest extends PHPUnit_Framework_TestCase
    {
        // Simple test for GetErrorCode to work Properly
        public function testGetErrorCode()
        {
            $TestClass = new QueryService();
            $this->assertEquals('One', $TestClass->GetErrorCode(1));
            $this->assertEquals('Two', $TestClass->GetErrorCode(2));
        }
    
        // Could also use dataProvider to send different returnValues, and then check with Asserts.
        public function testDoQuery()
        {
            // Create a mock for the IntegratedService class,
            // only mock the getResult() method.
            $MockService = $this->getMock('IntegratedService', array('getResult'));
    
            // Set up the expectation for the getResult() method 
            $MockService->expects($this->any())
                        ->method('getResult')
                        ->will($this->returnValue(1));
    
            // Create Test Object - Pass our Mock as the service
            $TestClass = new QueryService($MockService);
            // Or
            // $TestClass = new QueryService();
            // $TestClass->SetIntegratedServices($MockService);
    
            // Test DoQuery
            $QueryString = 'Some String since we did not specify it to the Mock';  // Could be checked with the Mock functions
            $this->assertEquals('One', $TestClass->DoQuery($QueryString));
        }
    }
    

    【讨论】:

      【解决方案2】:

      您需要使用PHPUnit 来创建您的测试对象。如果您告诉PHPUnit 您想模拟哪些方法,则仅模拟这些方法,其余的类方法将与原始类保持一致。

      所以一个示例测试可能如下所示:

      public function testDoQuery()
      {
          $queryService = $this->getMock('\QueryService', array('GetErrorCode')); // this will mock only "GetErrorCode" method
      
          $queryService->expects($this->once())
              ->method('GetErrorCode')
              ->with($this->equalTo($expectedErrorCode));
      }
      

      无论如何,正如上面的回答所说,您还应该使用Dependency Injection 模式以使模拟IntegratedService 成为可能(因为根据上面的示例,您需要知道$result->success 的值)。

      所以正确的测试应该是这样的:

      public function testDoQuery_Error()
      {
          $integratedService = $this->getMock('\IntegratedService', array('getResult'));
      
          $expectedResult = new \Result;
          $expectedResult->success = false;
      
          $integratedService->expects($this->any())
              ->method('getResult')
              ->will($this->returnValue($expectedResult));
      
          $queryService = $this->getMockBuilder('\QueryService')
              ->setMethods(array('GetErrorCode'))
              ->setConstructorArgs(array($integratedService))
              ->getMock();
      
          $queryService->expects($this->once())
              ->method('GetErrorCode')
              ->with($this->equalTo($expectedErrorCode))
              ->will($this->returnValue('expected error msg');
      
          $this->assertEquals($expectedResult->error, 'expected error msg');  
      }
      
      public function testDoQuery_Success()
      {
          $integratedService = $this->getMock('\IntegratedService', array('getResult'));
      
          $expectedResult = new \Result;
          $expectedResult->success = true;
      
          $integratedService->expects($this->any())
              ->method('getResult')
              ->will($this->returnValue($expectedResult));
      
          $queryService = $this->getMockBuilder('\QueryService')
              ->setMethods(array('GetErrorCode'))
              ->setConstructorArgs(array($integratedService))
              ->getMock();
      
          $queryService->expects($this->never())
              ->method('GetErrorCode');
      }
      

      【讨论】:

        猜你喜欢
        • 2017-01-13
        • 1970-01-01
        • 2017-08-25
        • 2016-01-30
        • 2015-10-03
        • 2020-05-08
        • 2015-12-18
        • 1970-01-01
        • 2013-02-23
        相关资源
        最近更新 更多