PHPUnit: How to mock a function on a class?

后端 未结 2 933
情书的邮戳
情书的邮戳 2021-01-17 04:33

I have a class called \"QueryService\". On this class, there is a function called \"GetErrorCode\". Also on this class is a function called \"DoQuery\". So you can safely sa

相关标签:
2条回答
  • 2021-01-17 04:59

    To test this class, you would mock the IntegratedService. Then, the IntegratedService::getResult() can be set to return what ever you like in a mock.

    Then testing becomes easier. You also need to be able to use Dependency Injection to pass the mocked service instead of the real one.

    Class:

    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
        }
    }
    

    Test:

    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));
        }
    }
    
    0 讨论(0)
  • 2021-01-17 05:20

    You need to use PHPUnit to create your Subject Under Test. If you tell PHPUnit which methods you would like to mock it mock only these method and the rest of class methods will stay as from original class.

    So an example test could look like this:

    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));
    }
    

    Anyway, as answer above says, you should also use Dependency Injection pattern in order to make possible mock IntegratedService as well (because based on the example above you need to know $result->success value).

    So the right test should look like this:

    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');
    }
    
    0 讨论(0)
提交回复
热议问题