PhpUnit Testing stub method multiple return

2.5k Views Asked by At

I am doing some PHPUnit testing with symfony2. I am having a problem with one particular test.

I am testing a response form one of my Class of course one response is true one false. I have a Mock of my Database and I have a stub for one of the methods from my databaseRepository.

The issue is that in one test I do a stub to a method with an valid array, second test i just want to query to be invalid null.

my db MOck:

    //Setting up mock of database repository class
    $this->db = $this->getMockBuilder('DatabaseRepository')
        ->disableOriginalConstructor()
        ->getMock();

    $this->db->expects($this->any())
        ->method('getRecord')
        ->will($this->returnValue(self::$registrationRecord));

    $this->db->expects($this->any())
        ->method('getRecord')
        ->willReturn(null);

So I am trying to have two different expects but this obviousley does not work.....is it possible to have a stub method to have two different returns..?

test1:

    <?php


class UnsubscribeRegistrationTemplateTest extends \PHPUnit_Framework_TestCase
{

    /**
     * @var UnsubscribeRegistrationTemplate
     */
    protected $object;

    /**
     * @var ValidationClass
     */
    public $validate;

    /**
     * @var DatabaseRepository
     */
    public $db;

    //Database Record Mock
    public static $registrationRecord = array
    (
        'rowid' => '96',
        'unsubscription' => 'N',
        'updated' => 'BB'
    );

    /**
     *
     */
    protected function setUp()
    {
        //Setting up mock of validation class
        $this->validate = $this->getMockBuilder('ValidationClass')
            ->disableOriginalConstructor()
            ->getMock();

        $this->validate->expects($this->any())
            ->method('validateInput')
            ->willReturn(true);

        //Setting up mock of database repository class
        $this->db = $this->getMockBuilder('DatabaseRepository')
            ->disableOriginalConstructor()
            ->getMock();

        $this->db->expects($this->any())
            ->method('getRegistrationRecord')
            ->will($this->returnValue(self::$registrationRecord));

        $this->db->expects($this->any())
            ->method('getRegistrationRecord')
            ->will($this->returnValue(null));

        $this->db->expects($this->any())
            ->method('setPreRegistrationEnquiryUnsubscriptionEnabled')
            ->willReturn(true);

        $this->object = $this->createUnsubscribeRegistrationTemplateInstance();
    }

    /**
     * @return UnsubscribeRegistrationTemplate
     *
     */
    public function createUnsubscribeRegistrationTemplateInstance()
    {
        //initialize Unsubscribe Registration Template
        return new UnsubscribeRegistrationTemplate
        (
            $this->validate,
            $this->db
        );
    }

    /**
     * @param array $mapping
     * @return Request
     */
    public function createRequest(array $mapping)
    {
        $request = new Request();

        foreach ( $mapping as $k =>$v)
        {
            $request->query->set($k, $v);
        }

        return $request;
    }

    /**
     *
     */
    public function testUnsubscribeRegistrationTemplateValidResponse()
    {
        $request = $this->createRequest(array(
            'registration_id' => '96',
            'source_channel' => 'BB'
        ));

        $response = new Response(
            true,
            'Unsubscription successful'
        );

        $this->assertEquals($response, $this->object->create($request));
    }

    /**
     *
     */
    public function testUnsubscribeRegistrationTemplateEmptyResponse()
    {
        $request = $this->createRequest(array(
            'registration_id' => '96',
            'source_channel' => 'BB'
        ));

        $response = new Response(
            false,
            'Registration Record Not Found.'
        );

        $this->assertEquals($response, $this->object->create($request));
    }

    /**
     *
     */
    public function testIsAlreadyRegisteredValidResponse()
    {
        //Testing record is already unsubscribed
        $registrationRecord = array(
            'unsubscription_enabled' => 'Y'
        );

        $this->assertTrue($this->object->isAlreadyUnsubscribed($registrationRecord));
    }

    /**
     *
     */
    public function testIsAlreadyRegisteredInValidResponse()
    {
        //Testing record not unsubscribed
        $registrationRecord = array(
            'unsubscription_enabled' => 'N'
        );
        $this->assertFalse($this->object->isAlreadyUnsubscribed($registrationRecord));
    }

    /**
     *
     */
    protected function tearDown()
    {
        unset($this->object);
    }

}
2

There are 2 best solutions below

2
On BEST ANSWER

You can do this in many ways.
Here are two ways that may suits your needs.

1 - Move the getRecord() expects to the tests

/**
* @test
*/
public function ifTrue()
{
    $this->db->expects($this->once())
    ->method('getRecord')
    ->will($this->returnValue(self::$registrationRecord));

    $request = $this->createRequest(array(
        'id' => '10',
        'code' => 'BB'
    ));

    $response = new Response(
        true,
        'successful'
    );

    $this->assertEquals($response, $this->object->create($request));
}

/**
 * @test
 */
public function ifFalse()
{
    $this->db->expects($this->once())
    ->method('getRecord')
    ->willReturn(null);

    $request = $this->createRequest(array(
        'id' => '10',
        'code' => 'BB'
    ));

    $response = new Response(
        false,
        'Record Not Found.'
    );

    $this->assertEquals($response, $this->object->create($request));
}

As you can see, there's a lot of duplication, so let's use dataprovider.

2 - Using @dataProvider

protected function getDataForTest()
{
    return array(
        array(self::$registrationRecord, true, 'successful'),
        array(null, false, 'Record Not Found.')
    );
}

/**
* @dataProvider getDataForTest
* @test
*/
public function ifTrue($getRecordValue, $bool, $message)
{
    $this->db->expects($this->once())
    ->method('getRecord')
    ->will($this->returnValue($getRecordValue);

    $request = $this->createRequest(array(
        'id' => '10',
        'code' => 'BB'
    ));

    $response = new Response(
        $bool,
        $message
    );

    $this->assertEquals($response, $this->object->create($request));
}

Using @dataProvider you can use as many values as you want to test all cases.

7
On

I think you should use the PHPUnit at() method to check method invocation at certain index. So you must substitute the expects value argument with the correct index's call.

So you can use the following code:

//Setting up mock of database repository class
$this->db = $this->getMockBuilder('DatabaseRepository')
    ->disableOriginalConstructor()
    ->getMock();

$this->db->expects($this->at(0)) // Mock the  first call
    ->method('getRecord')
    ->will($this->returnValue(self::$registrationRecord));

$this->db->expects($this->at(1)) // Mock the  second call
    ->method('getRecord')
    ->willReturn(null);

http://www.andrejfarkas.com/2012/07/phpunit-at-method-to-check-method-invocation-at-certain-index/

Hope this help