Search code examples
phpzend-framework2phpunitzend-db

PHPUnit - mock callback function


Here is a function which uses Zend DB / Tablegateway :

public function listAttestations($sSidx = null, $sSord = null, $iOffset = 0, $iLimit = 0)
{
    try {
        $resultSet = $this->tableGateway->select(
            function (Select $select) use ($sSidx, $sSord, $iOffset, $iLimit) {
                if ($sSidx != null && $sSord != null) {
                    $select->order($sSidx.' '.$sSord);
                }
                $select->join(
                    'f_travclient',
                    'syndic_client_id = f_travclient.travClient_id',
                     array('syndic' => 'nom')
                );
                $select->offset($iOffset);
                $select->limit($iLimit);
            }
        );
        return $resultSet;
    } catch (\Exception $e) {
        throw new \Exception($e);
    }
}

I use PHPUnit to do unit tests. Perhaps, I don't know how to make the function which crosses my previous method. I thought this could be functional :

public function testListAttestations()
{
    $resultSet = new ResultSet();

    $mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway', array('select'), array(), '', false);

    $mockTableGateway->expects($this->once())
            ->method('select')
            ->with()
            ->will($this->returnValue($resultSet));

    $attestTable = new FMaiAttestationTable($mockTableGateway, $this->adapter, $this->sql);

    $this->assertSame($resultSet, $attestTable->listAttestations('maiAttestation_id', 'ASC', 0, 30));
}

But this doesn't go further the :

function (Select $select) use ($sSidx, $sSord, $iOffset, $iLimit) {

Could somebody help me ? Thanks.


Solution

  • You can fetch and use any arguments given to a mocked method with returnCallback():

    $mockTableGateway->expects($this->once())
            ->method('select')
            ->with()
            ->will($this->returnCallback(function($function) use ($resultSet) {
                // do something with the function, for example:
                if(!is_callable($function)) {
                    return NULL;
                }
                call_user_func($function, new Select());
                return $resultSet;
            }));
    

    Still, you might want to reconsider your current code as it is not necessary to write it nested like this. You could, for example, get an instance of Select yourself and use it with selectWith()

    public function listAttestations($sSidx = null, $sSord = null, $iOffset = 0, $iLimit = 0)
    {
        try {
            $select = new Select();
            if ($sSidx != null && $sSord != null) {
                $select->order($sSidx.' '.$sSord);
            }
            $select->join(
                'f_travclient',
                'syndic_client_id = f_travclient.travClient_id',
                array('syndic' => 'nom')
            );
            $select->offset($iOffset);
            $select->limit($iLimit);
            $resultSet = $this->tableGateway->selectWith($select);
            return $resultSet;
        } catch (\Exception $e) {
            throw new \Exception($e);
        }
    }
    

    In this case it is now possible to test if your method puts the Select together the way you want. You can simply create another Select object in your test the way you expect it to be:

        $resultSet = new ResultSet();
    
        $sSidx = 'maiAttestation_id';
        $sSord = 'ASC';
        $iOffset = 0;
        $iLimit = 30;
    
        $select = new Select();
        $select->order($sSidx.' '.$sSord);
        $select->join(
            'f_travclient',
            'syndic_client_id = f_travclient.travClient_id',
            array('syndic' => 'nom')
        );
        $select->offset($iOffset);
        $select->limit($iLimit);
    
        $mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway',
            array('selectWith'), array(), '', false);
    
        $mockTableGateway->expects($this->once())
            ->method('selectWith')
            ->with($select)
            ->will($this->returnValue($resultSet));
    
        $attestTable = new FMaiAttestationTable($mockTableGateway, $this->adapter, $this->sql);
    
        $this->assertEquals($resultSet, $attestTable->listAttestations($sSidx, $sSord, $iOffset, $iLimit));
    

    If the Select object used by the mocked method selectWith looks any different from the one you created, PHPUnit will throw an error in your test.