代码之家  ›  专栏  ›  技术社区  ›  rr.

PHPUnit模拟对象和静态方法

  •  43
  • rr.  · 技术社区  · 14 年前

    class Model_User extends Doctrine_Record
    {
        public static function create($userData)
        {
            $newUser = new self();
            $newUser->fromArray($userData);
            $newUser->save();
        }
    }
    

    理想情况下,我会使用模拟对象来确保 fromArray (和)提供给用户的数据 save

    有什么建议吗?

    6 回复  |  直到 4 年前
        1
  •  49
  •   k0pernikus    9 年前

    《PHPUnit》的作者塞巴斯蒂安·伯格曼(Sebastian Bergmann)最近发表了一篇关于 Stubbing and Mocking Static Methods . 通过phpunit3.5和php5.3以及后期静态绑定的一致使用,您可以

    $class::staticExpects($this->any())
          ->method('helper')
          ->will($this->returnValue('bar'));
    

    staticExpects deprecated as of PHPUnit 3.8 并将在以后的版本中完全删除。

        2
  •  14
  •   treeface    10 年前

    https://github.com/Codeception/AspectMock

    $this->assertEquals('users', UserModel::tableName());   
    $userModel = test::double('UserModel', ['tableName' => 'my_users']);
    $this->assertEquals('my_users', UserModel::tableName());
    $userModel->verifyInvoked('tableName'); 
    
        3
  •  4
  •   observer Darrel Miller    4 年前

    我将在单元测试名称空间中创建一个新类,扩展 Model_User 并对此进行测试。下面是一个例子:

    原始类别:

    class Model_User extends Doctrine_Record
    {
        public static function create($userData)
        {
            $newUser = new self();
            $newUser->fromArray($userData);
            $newUser->save();
        }
    }
    

    要在单元测试中调用的模拟类:

    use \Model_User
    class Mock_Model_User extends Model_User
    {
        /** \PHPUnit\Framework\TestCase */
        public static $test;
    
        // This class inherits all the original classes functions.
        // However, you can override the methods and use the $test property
        // to perform some assertions.
    }
    

    在单元测试中:

    use Module_User;
    use PHPUnit\Framework\TestCase;
    
    class Model_UserTest extends TestCase
    {
        function testCanInitialize()
        {   
            $userDataFixture = []; // Made an assumption user data would be an array.
            $sut = new Mock_Model_User::create($userDataFixture); // calls the parent ::create method, so the real thing.
    
            $sut::test = $this; // This is just here to show possibilities.
    
            $this->assertInstanceOf(Model_User::class, $sut);
        }
    }
    
        4
  •  1
  •   gealex    6 年前

    这个 doublit

    /* Create a mock instance of your class */
    $double = Doublit::mock_instance(Model_User::class);
    
    /* Test the "create" method */
    $double::_method('create')
       ->count(1) // test that the method is called once
       ->args([Constraints::isInstanceOf('array')]) // test that first argument is an array
       ->stub('my_value') // stub the method to return "myvalue"
    
        5
  •  0
  •   Mikkel    8 年前

    另一种可能的方法是使用 Moka 图书馆:

    $modelClass = Moka::mockClass('Model_User', [ 
        'fromArray' => null, 
        'save' => null
    ]);
    
    $modelClass::create('DATA');
    $this->assertEquals(['DATA'], $modelClass::$moka->report('fromArray')[0]);
    $this->assertEquals(1, sizeof($modelClass::$moka->report('save')));
    
        6
  •  0
  •   Serhii Vasko    4 年前

    再一个 approach :

    class Experiment
    {
        public static function getVariant($userId, $experimentName) 
        {
            $experiment = self::loadExperimentJson($experimentName):
            return $userId % 10 > 5;  // some sort of bucketing
        } 
    
        protected static function loadExperimentJson($experimentName)
        {
            // ... do something
        }
    }
    

    在我的test.php中

    class ExperimentTest extends \Experiment
    {
        public static function loadExperimentJson($experimentName) 
        {
            return "{
                "name": "TestExperiment",
                "variants": ["a", "b"],
                ... etc
            }"
        }
    }
    

    public function test_Experiment_getVariantForExperiment()
    {
        $variant = ExperimentTest::getVariant(123, 'blah');
        $this->assertEquals($variant, 'a');
    
        $variant = ExperimentTest::getVariant(124, 'blah');
        $this->assertEquals($variant, 'b');
    }
    
        7
  •  0
  •   Ilia Yatsenko    3 年前

    找到了有效的解决方案,尽管话题很老,但还是愿意与大家分享。 class_alias 可以替换尚未自动加载的类(仅在使用自动加载时有效,不直接包含/需要文件)。 例如,我们的代码:

    class MyClass
    {
       public function someAction() {
          StaticHelper::staticAction();
       }
    }
    

    我们的测试:

    class MyClassTest 
    {
       public function __construct() {
          // works only if StaticHelper is not autoloaded yet!
          class_alias(StaticHelperMock::class, StaticHelper::class);
       }
    
       public function test_some_action() {
          $sut = new MyClass();
          $myClass->someAction();
       }
    }
    

    class StaticHelperMock
    {
       public static function staticAction() {
          // here implement the mock logic, e.g return some pre-defined value, etc 
       }
    }
    

    这个简单的解决方案不需要任何特殊的lib或扩展。

        8
  •  -1
  •   Pascal MARTIN    14 年前

    测试静态方法通常被认为有点困难 (你可能已经注意到了)


    例如,像这样的事情能不能不起作用:

    class Model_User extends Doctrine_Record
    {
        public function saveFromArray($userData)
        {
            $this->fromArray($userData);
            $this->save();
        }
    }
    

    不确定要测试什么;但是,至少,没有静态方法了。。。