【问题标题】:PhpUnit Testing stub method multiple returnPhpUnit 测试存根方法多次返回
【发布时间】:2015-08-23 05:53:19
【问题描述】:

我正在用 symfony2 做一些 PHPUnit 测试。我对一项特定的测试有疑问。

我正在测试我的班级之一的回答,当然一个回答是对的,一个是假的。我有一个我的数据库的模拟,并且我有一个用于我的 databaseRepository 中的一种方法的存根。

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

我的数据库模拟:

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

}

【问题讨论】:

  • 在相关测试方法中移动expect,例如,如果testIsAlreadyRegisteredInValidResponse希望getRecord返回null,将代码移到那里并从设置中删除另一个expect。
  • 是的,我以前这样做过,但是我的 PhpStorm 突出显示了 expect 函数,虽然它不起作用,所以甚至从来没有尝试过:/ 现在我做到了,它工作得很好,但是 phpstorm hihglights它说:在类 databaseRepository 中找不到方法期望
  • 别担心,这只是因为 phpstorm 不理解正确的对象类型,可能是由于错误或丢失了 PHPDoc 注释。所以你解决了?
  • 是的,我确实投了赞成票,但平心而论,@Pierre Marichez 一开始就提到了这种形式
  • 当然,皮埃尔马上明白你有什么问题。重要的是您解决了问题并...您了解如何更好地使用测试框架!

标签: php unit-testing symfony phpunit


【解决方案1】:

您可以通过多种方式做到这一点。
这里有两种方法可能适合您的需求。

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

如您所见,有很多重复,所以我们使用dataprovider

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,您可以使用任意数量的值来测试所有案例。

【讨论】:

  • 嘿,谢谢你的回答`我已经尝试过第一种方法,但它接着说:期望在课堂上找不到
  • 您在何时何地实例化您的 dbMock?
【解决方案2】:

我认为您应该使用 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,为了复制您的问题,请发布您可以发布的所有代码,这些代码对本地执行您的场景很有用
猜你喜欢
  • 1970-01-01
  • 2013-06-20
  • 1970-01-01
  • 2021-02-12
  • 2015-02-15
  • 1970-01-01
  • 2020-12-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多