PhpUnit测试桩方法多次返回

3

我正在使用Symfony2进行PHPUnit测试,遇到了一个问题。

我正在对我的一个类进行响应测试,其中一个响应返回true,另一个返回false。 我有一个模拟的数据库和一个数据库Repository方法的桩。

问题在于,在一个测试中,我对具有有效数组的方法进行了桩,而在另一个测试中,我只想查询为无效null。

我的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);

所以我试图有两个不同的期望值,但显然这是行不通的......是否可能有一个存根方法可以有两个不同的返回值..?
测试1:
    <?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);
    }

}

将期望值移动到相关的测试方法中,例如,如果testIsAlreadyRegisteredInValidResponse希望getRecord返回null,则将代码移到那里,并从设置中删除其他期望值。 - Matteo
是的,我以前做过这个,但是我的 PhpStorm 高亮了 expect 函数,所以我认为它不会起作用,所以甚至没有尝试 :/ 现在我尝试了一下,它完美运行了,但是 php storm 引用说:类 databaseRepository 中没有找到 expect 方法。 - Koper
不用担心,这只是因为PhpStorm可能由于错误或缺失的PHPDoc注释而无法理解正确的对象类型。那么你解决了吗? - Matteo
是的,我做了,谢谢你的投票,但公平地说,@Pierre Marichez 从一开始就提到了这个表单。 - Koper
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Matteo
2个回答

3

你有很多种方法可以实现这个操作。
下面是两种可能符合你需求的方式。

1 - 将getRecord()期望的内容移至测试中

/**
* @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));
}

如您所见,有很多重复的内容,因此让我们使用数据提供器

2 - 使用 @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));
}

使用 @dataProvider,您可以使用尽可能多的值来测试所有情况。


嘿,谢谢你的回答。我已经尝试了第一种方法,但它显示:在类中找不到expect。 - Koper
你在哪里和何时实例化你的dbMock? - Pierre Marichez

3
我认为您应该使用PHPUnit的at()方法来检查在特定索引处对方法的调用。因此,您必须将expects值参数替换为正确索引的调用。

因此,您可以使用以下代码:

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

希望这能有所帮助。


嘿,谢谢你的回复。实际上我尝试了这个解决方案,可能应该提到,但它没有起作用,我收到了这条消息:d>在序列索引0处调用时。 预期在索引0处调用的操作从未被执行到。 - Koper
@Koper,你能发布整个测试类吗? - Matteo
我的测试方法或我正在尝试存根的方法 - Koper
@Koper,为了复制您的问题,请发布所有可用于本地执行您的场景的代码。 - Matteo
@Koper,你需要编辑你的问题,而不是我的回答! :) - Matteo
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接