PHP 重载以单元测试私有属性和方法

发布于 2024-10-18 07:22:53 字数 1066 浏览 1 评论 0原文

我在这里阅读了一些有关使用单元测试来测试私有方法和属性的问题。我是单元测试的新手,希望输入我正在尝试的方法,以便我的测试可以访问私有/受保护的属性和方法。

在我正在进行的测试中,我想确认将特定参数传递给对象会导致设置属性。我使用 SimpleTest 进行单元测试教育,我的测试方法如下:

function test__Construction_Should_Properly_Set_Tables() {
  $cv = new CVObject( array( 'tables' => $this->standardTableDef ) );
  $tables = $cv->tables;
  $this->assertEqual( $tables, $this->standardTableDef );
}

然后我在 CVObject 中编写了一个 __get 方法,如下所示:

function __get( $name ) {
  $trace = debug_backtrace();
  $caller = $trace[1];
  $inTesting = preg_match( '/simpletest/', $caller['file'] );

  if ( $inTesting ) {
    return $this->$name;
  } else {
    trigger_error( 'Cannot access protected property CVObject::$' .
                     $name . ' in ' . $trace[0]['file'] . ' on line ' .
                     $trace[0]['line'],
                    E_USER_NOTICE );
  }
}

我的想法是,如果调用文件来自 SimpleTest,则继续并使属性可用出于测试目的,但如果没有,则触发错误。这允许我保持属性私有,但能够在测试中使用它,这对于我即将开始编写的特定私有方法来说更加重要。

所以,我的问题是,我是否在这里错过了一些非常糟糕的东西并且应该避免这种技术?

I've been reading here a few questions regarding the use of unit testing to test private methods and properties. I'm new to unit testing and would like input on the method I'm trying so that my testing can access private/protected properties and methods.

In the test I was working on I wanted to confirm that passing a particular parameter to the object resulted in a property being set. I'm using SimpleTest for my unit testing education and my test method is as follows:

function test__Construction_Should_Properly_Set_Tables() {
  $cv = new CVObject( array( 'tables' => $this->standardTableDef ) );
  $tables = $cv->tables;
  $this->assertEqual( $tables, $this->standardTableDef );
}

Then I wrote a __get method in CVObject as follows:

function __get( $name ) {
  $trace = debug_backtrace();
  $caller = $trace[1];
  $inTesting = preg_match( '/simpletest/', $caller['file'] );

  if ( $inTesting ) {
    return $this->$name;
  } else {
    trigger_error( 'Cannot access protected property CVObject::

My idea in this is that if the calling file is from SimpleTest, go ahead and make the property available for the testing purposes, but if not, trigger the error. This allows me to keep the property private but be able to use it in testing, which is going to be more important to me with a particular private method I'm about to begin writing.

So, my question is, am I missing something really bad here and should avoid this technique?

. $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE ); } }

My idea in this is that if the calling file is from SimpleTest, go ahead and make the property available for the testing purposes, but if not, trigger the error. This allows me to keep the property private but be able to use it in testing, which is going to be more important to me with a particular private method I'm about to begin writing.

So, my question is, am I missing something really bad here and should avoid this technique?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

才能让你更想念 2024-10-25 07:22:53

如果您发现自己陷入困境,并且必须访问私有/受保护的属性才能进行彻底的测试,至少将支持访问的代码放置在您的测试或测试框架中。在生产代码中嵌入仅用于测试的代码 a) 使设计变得复杂,b) 添加了更多必须测试的代码,c) 意味着代码在生产中的运行方式不同。

您可以对受保护的属性使用 Ken 的子类方法,但如果您需要访问私有并且使用 PHP 5.3.2+,则可以使用反射。

function test__Construction_Should_Properly_Set_Tables() {
    $cv = new CVObject( array( 'tables' => $this->standardTableDef ) );
    $tables = self::getPrivate($cv, 'tables');
    $this->assertEqual( $tables, $this->standardTableDef );
}

static function getPrivate($object, $property) {
    $reflector = new ReflectionProperty(get_class($object), $property);
    $reflector->setAccessible(true);
    return $reflector->getValue($object);
}

请注意,getPrivate() 不会像为从超类继承的属性所编写的那样工作,但循环层次结构以查找声明类并不难。

If you find yourself stuck and simply must access a private/protected property to enable thorough testing, at least place the code that enables access in your test or testing framework. Embedding testing-only code in production code a) complicates the design, b) adds more code that must be tested, and c) means the code runs differently in production.

You can use Ken's subclass method for protected properties, but if you need to access private and are on PHP 5.3.2+ you can use reflection.

function test__Construction_Should_Properly_Set_Tables() {
    $cv = new CVObject( array( 'tables' => $this->standardTableDef ) );
    $tables = self::getPrivate($cv, 'tables');
    $this->assertEqual( $tables, $this->standardTableDef );
}

static function getPrivate($object, $property) {
    $reflector = new ReflectionProperty(get_class($object), $property);
    $reflector->setAccessible(true);
    return $reflector->getValue($object);
}

Note that getPrivate() won't work as written for properties inherited from superclasses, but it's not too hard to loop up the hierarchy to find the declaring class.

北风几吹夏 2024-10-25 07:22:53

在测试一个组件时,你必须只测试它的接口(输入、输出、异常),而不考虑甚至不知道它的内部实现(如果一个程序员编写测试用例,另一个程序员实现实现,那就更好了,请参考 XP 和 TDD技术)。因此,您唯一需要测试的是公共方法。

为了确保您的私有(帮助程序)方法正确编写,只需使用代码覆盖率分析器(请查看 PHP 的代码覆盖工具)并在测试用例中覆盖尽可能多的代码。

您的解决方案将给您带来维护噩梦。测试用例和组件实现不应该以任何方式耦合,因为耦合需要是防弹的,否则您也必须对其进行测试。

While testing a component, you have to test only it's interface (input, output, exceptions), without considering or even knowing it's internal implementation (even better if one programmer writes test cases and the other does the implementation, please refer to XP and TDD techniques). So, the only thing you have to test are public methods.

To ensure, that your private (helper) methods are written correctly, simply use code coverage analyzer (please checkout Code Coverage tools for PHP) and cover as much code as possible with your test cases.

Your solution will guarantee you a maintenance nightmare. Test cases and component implementation should not be coupled in any way, because coupling would need to be bulletproof or otherwise you'll have to test it too.

神经大条 2024-10-25 07:22:53

一个快速但肮脏的解决方案是使用受保护的(而不是私有的)方法,然后使用使被测方法公开的包装器进行测试。

class Foo
{
    protected function bar(){} // should really be private but protected will do
}

class FooTestWrapper extends Foo
{
    public function bar{} { return parent::bar(); } // this is testable
}

但正如 ljank 指出的那样,测试私有方法/实现可能会成为维护噩梦 - 这可能意味着您正在做的工作应该被外包给其他班级。

A quick and dirty solution is to use protected (instead of private) methods, and then test using a wrapper that makes the methods under test public.

class Foo
{
    protected function bar(){} // should really be private but protected will do
}

class FooTestWrapper extends Foo
{
    public function bar{} { return parent::bar(); } // this is testable
}

But as ljank points out, testing private methods/implementation can become a maintenance nightmare - it probably means you are doing work that should be farmed out to other classes.

韵柒 2024-10-25 07:22:53

一般来说,您的代码不应包含仅用于测试的功能。虽然测试私有/受保护方法是否是一种好的做法存在争议,但我发现有时并且想要单独测试某个私有/受保护方法。

使用 Netsilik/BaseTestCase (MIT 许可证),您可以设置/获取私有变量并调用私有或受保护的函数:

作曲家需要 netsilik/base-test-case

要测试的类:

<?php
namespace App;

class Foo
{
    private $bar;

    protected function setBar(string $bar)
    {
        $this->bar = $bar;
    }
}

单元测试:

class MyTestCase extends \Netsilik\Testing\BaseTestCase
{
    public function test_whenProtectedMethodCalled_thenPrivateMemberSet()
    {
        $foo = new Foo();

        self::callInaccessibleMethod($foo, 'setBar', 'abc');

        self::assertEquals('abc', self::getInaccessibleProperty($foo, 'bar'));
    }
}

希望这有帮助。

Generally speaking, your code should not contain features just for testing. And while it is debatable if testing private/protected methods is good practice, I find that sometimes and want to test a certain private/protected method in isolation.

Using Netsilik/BaseTestCase (MIT License) you can set/get private variable and call private or protected functions:

composer require netsilik/base-test-case

Class to test:

<?php
namespace App;

class Foo
{
    private $bar;

    protected function setBar(string $bar)
    {
        $this->bar = $bar;
    }
}

Unit test:

class MyTestCase extends \Netsilik\Testing\BaseTestCase
{
    public function test_whenProtectedMethodCalled_thenPrivateMemberSet()
    {
        $foo = new Foo();

        self::callInaccessibleMethod($foo, 'setBar', 'abc');

        self::assertEquals('abc', self::getInaccessibleProperty($foo, 'bar'));
    }
}

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文