IT

PHPUunit 모의 객체 및 정적 방법

itgroup 2023. 7. 27. 21:53
반응형

PHPUunit 모의 객체 및 정적 방법

다음 정적 방법을 테스트하는 가장 좋은 방법을 찾고 있습니다(특히 독트린 모델 사용).

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

이상적으로, 나는 모의 물체를 사용하여 그것을 확실히 할 것입니다.fromArray(제공된 사용자 데이터 포함) 및save호출되었지만 메서드가 정적이므로 불가능합니다.

좋은 의견이라도 있나?

PHP 유닛의 저자인 Sebastian Bergmann은 최근 Stubbing과 Mocking Static Methods에 대한 블로그 게시물을 가지고 있습니다.PHPUnit 3.5 및 PHP 5.3을 사용하고 최신 정적 바인딩을 일관되게 사용하면 다음과 같은 작업을 수행할 수 있습니다.

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

업데이트: staticExpectsPHPUit 3.8에서 더 이상 사용되지 않으며 이후 버전에서는 완전히 제거될 예정입니다.

이제 AspectMock 라이브러리를 사용하여 다음 작업을 수행할 수 있습니다.

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

나는 유닛 테스트 네임스페이스에서 확장된 새로운 클래스를 만들 것입니다.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);
    }
}

작업 솔루션을 찾았습니다. 주제가 오래되었음에도 불구하고 공유하고 싶습니다.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 
   }
}

이 간단한 솔루션에는 특별한 립이나 확장이 필요하지 않습니다.

Mockery의 별칭 기능을 사용하여 공개 정적 메서드를 모의할 수 있습니다.

http://docs.mockery.io/en/latest/reference/creating_test_doubles.html#creating-test-doubles-aliasing

다른 가능한 접근 방식은 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')));

한 가지 더 접근 방식:

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

내 실험 테스트에서.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');
}

정적 방법을 테스트하는 것은 일반적으로 (아마도 이미 알고 있었겠지만) 특히 PHP 5.3 이전에는 좀 어려운 것으로 간주됩니다.

정적 방법을 사용하지 않도록 코드를 수정할 수 없습니까? 사실 여기서 정적 방법을 사용하는 이유를 잘 모르겠습니다. 이것은 아마도 정적이 아닌 코드로 다시 작성될 수도 있습니다. 그렇지 않나요?


예를 들어, 다음과 같은 것이 효과를 발휘하지 못할 수도 있습니다.

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

무엇을 테스트할지 확신할 수 없지만, 적어도 더 이상 정적인 방법은 없습니다.

언급URL : https://stackoverflow.com/questions/2357001/phpunit-mock-objects-and-static-methods

반응형