PHPUnit Mock Objects and Static Methods

后端 未结 7 1326
梦谈多话
梦谈多话 2020-11-27 18:02

I am looking for the best way to go about testing the following static method (specifically using a Doctrine Model):

class Model_User extends Doctrine_Record         


        
相关标签:
7条回答
  • 2020-11-27 18:40

    Sebastian Bergmann, the author of PHPUnit, recently had a blog post about Stubbing and Mocking Static Methods. With PHPUnit 3.5 and PHP 5.3 as well as consistent use of late static binding, you can do

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

    Update: staticExpects is deprecated as of PHPUnit 3.8 and will be removed completely with later versions.

    0 讨论(0)
  • 2020-11-27 18:41

    I would make a new class in the unit test namespace that extends the Model_User and test that. Here's an example:

    Original class:

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

    Mock Class to call in unit test(s):

    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.
    }
    

    In your unit test:

    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);
        }
    }
    
    0 讨论(0)
  • 2020-11-27 18:42

    Another possible approach is with the Moka library:

    $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')));
    
    0 讨论(0)
  • 2020-11-27 18:55

    There is now the AspectMock library to help with this:

    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'); 
    
    0 讨论(0)
  • 2020-11-27 18:56

    The doublit library could also help you to test static methods :

    /* 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"
    
    0 讨论(0)
  • 2020-11-27 19:00

    One more 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
        }
    }
    

    In my ExperimentTest.php

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

    And then I would use it like so:

    public function test_Experiment_getVariantForExperiment()
    {
        $variant = ExperimentTest::getVariant(123, 'blah');
        $this->assertEquals($variant, 'a');
    
        $variant = ExperimentTest::getVariant(124, 'blah');
        $this->assertEquals($variant, 'b');
    }
    
    0 讨论(0)
提交回复
热议问题