如何模拟在PHPUnit中返回新类的参数?

时间:2011-06-08 15:44:12

标签: php unit-testing mocking arguments phpunit

我遇到一行代码问题,如:

$user->has('roles', ORM::factory('role', array('name' => 'unverified')))

我可以模拟第一个参数,但只能断言第二个参数返回一个类。在某些类中,我对has进行了多次使用,并且需要能够正确地测试它们。为了清楚起见,我需要确认“未验证”被传递到第二个参数的工厂方法中。非常感谢任何帮助。

我正在测试的课程:

<?php
/**
 * Policy class to determine if a user can upload a file.
 */
class Policy_File_Upload extends Policy
{
    const NOT_LOGGED_IN = 1;
    const NOT_VERIFIED = 2;

    public function execute(Model_ACL_User $user, array $extra = NULL)
    {
        if ($user->can('login'))
        {
            return self::NOT_LOGGED_IN;
        }
        elseif ($user->has('roles', ORM::factory('role', array('name' => 'unverified'))))
        {
            return self::NOT_VERIFIED;
        }

        return TRUE;
    }
}

和相应的测试:

public function test_guest()
{
    // User mock
    $user = $this->getMock('Model_User', array('can', 'has'), array(), '', FALSE);
    $user->expects($this->any())->method('can')->with('login')->will($this->returnValue(TRUE));
    $user->expects($this->any())->method('has')->with('roles', $this->logicalOr
    (

    ))
    ->will($this->returnCallback(array($this, 'roles')));


    $policy = new Policy_File_Upload;

    $this->assertSame(Policy_File_Upload::NOT_VERIFIED, $policy->execute($user));
}

3 个答案:

答案 0 :(得分:2)

您可以随时使用数据驱动的路径为每个案例准备测试数据,并验证调用该函数的结果。最后,您并不关心'unverified'传递给ORM::factory(),而是基于输入从execute()得出正确的结果。

测试实现细节会使测试变得脆弱且难以编写。如果您可以测试结果,那么当您更改到达结果的方式时,测试不会中断。为所有用例组合数据集可能需要更长的时间,但通常可以在测试之间共享。

答案 1 :(得分:1)

  

要清楚,我需要确认“未验证”已传递到第二个参数的工厂方法。

<?php

public function execute(Model_ACL_User $user, array $extra = NULL)
    [...] ORM::factory('role', array('name' => 'unverified')) [...]

我很抱歉,你不能这样做,因为没有(理智的)方法来模拟静态方法调用。

为什么这是一个问题的论点类似于Sebastian Bergmanns博客文章之一:Testing-Code-That-Uses-Singletons

有一些选项如runkit,您可以选择在运行时将“ORM”类替换为模拟版本,但这确实非常痛苦。

所以有几个选择:

您可以创建类似 Policy_File_Upload_Data_Provider 的内容,其中包含 ->getRoles 方法,您可以在其中放置ORM访问权限。这将允许您很好地测试您的业务逻辑并测试数据访问类应该更容易,因为它没有那么多(只是访问orm)。

你可以注入你的ORM类(或者如果它不能解决它的名字)。 根据它自己的类,您应该能够创建一个实例并调用 - &gt;像它一样的工厂是一种常规方法。你可以嘲笑那个。

如果你不想让它留在那里也有一个不太糟糕的选择。

希望至少能给你一个想法/概述。


其他链接为什么很难测试静态:

Misko Hevery - Flaw: Brittle Global State & Singletons

Kore Nordmann - Static considered harmful

答案 2 :(得分:1)

您实际上可以使用parameter capturing中的Phake mocking framework为PHP传递给模拟方法的参数进行验证。

public function test_guest()
{
    // User mock
    $user = Phake::mock('Model_User');
    when($user)->can('login')->thenReturn(FALSE);
    when($user)->has('roles', $this->anything())->thenGetReturnByLambda(array($this, 'roles'));

    $policy = new Policy_File_Upload;

    $this->assertSame(Policy_File_Upload::NOT_VERIFIED, $policy->execute($user));

    Phake::verify($user)->has('roles', Phake::capture($factoriedRole));

    $this->assertEquals('unverified', $factoriedRole->get('name'));
}

这个例子假设您能够检查$ factoriedRole上的名称,显然该部分需要根据ORM堆栈的工作方式进行更改。

小记,我同意edorian关于静力学。如果可能的话,我总是希望重新设计你的代码而不需要静态,但有时这是不可能的,在这种情况下,这是一个可行的解决方案。

Phake Github Page

Phake Documentation on Parameter Capturing