GrG*_*rGr 275 php phpunit unit-testing
我发现讨论你是否测试私人方法的信息.
我已经决定,在某些类中,我想要保护方法,但测试它们.其中一些方法是静态的和简短的.由于大多数公共方法都使用它们,我可能会在以后安全地删除测试.但是对于从TDD方法开始并避免调试,我真的想测试它们.
我想到了以下几点:
哪个是最佳做法?还有别的事吗?
看来,JUnit会自动将受保护的方法更改为公开,但我没有深入了解它.PHP不允许通过反射.
uck*_*man 403
如果您在PHPUnit中使用PHP5(> = 5.3.2),则可以在运行测试之前使用反射将它们设置为公共,从而测试私有和受保护的方法:
protected static function getMethod($name) {
$class = new ReflectionClass('MyClass');
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
public function testFoo() {
$foo = self::getMethod('foo');
$obj = new MyClass();
$foo->invokeArgs($obj, array(...));
...
}
Run Code Online (Sandbox Code Playgroud)
tro*_*skn 47
你似乎已经意识到了,但我还是会重申它; 如果您需要测试受保护的方法,这是一个不好的迹象.单元测试的目的是测试类的接口,受保护的方法是实现细节.也就是说,有些情况下它是有道理的.如果使用继承,则可以看到超类为子类提供接口.所以在这里,你必须测试受保护的方法(但绝不是私有方法).解决方案是创建一个用于测试目的的子类,并使用它来公开方法.例如.:
class Foo {
protected function stuff() {
// secret stuff, you want to test
}
}
class SubFoo extends Foo {
public function exposedStuff() {
return $this->stuff();
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,您始终可以使用合成替换继承.在测试代码时,处理使用此模式的代码通常要容易得多,因此您可能需要考虑该选项.
小智 37
特斯特本有正确的方法.更简单的是直接调用方法并返回答案:
class PHPUnitUtil
{
public static function callMethod($obj, $name, array $args) {
$class = new \ReflectionClass($obj);
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method->invokeArgs($obj, $args);
}
}
Run Code Online (Sandbox Code Playgroud)
您可以通过以下方式在测试中简单地调用它:
$returnVal = PHPUnitUtil::callMethod(
$this->object,
'_nameOfProtectedMethod',
array($arg1, $arg2)
);
Run Code Online (Sandbox Code Playgroud)
tea*_*urn 20
我想对uckelman的答案中定义的getMethod()提出一个细微的变化.
此版本通过删除硬编码值并稍微简化使用来更改getMethod().我建议将它添加到您的PHPUnitUtil类中,如下例所示,或者添加到PHPUnit_Framework_TestCase扩展类(或者,我想,全局到您的PHPUnitUtil文件).
因为无论如何都要实例化MyClass,而ReflectionClass可以带一个字符串或一个对象......
class PHPUnitUtil {
/**
* Get a private or protected method for testing/documentation purposes.
* How to use for MyClass->foo():
* $cls = new MyClass();
* $foo = PHPUnitUtil::getPrivateMethod($cls, 'foo');
* $foo->invoke($cls, $...);
* @param object $obj The instantiated instance of your class
* @param string $name The name of your private/protected method
* @return ReflectionMethod The method you asked for
*/
public static function getPrivateMethod($obj, $name) {
$class = new ReflectionClass($obj);
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
// ... some other functions
}
Run Code Online (Sandbox Code Playgroud)
我还创建了一个别名函数getProtectedMethod()来明确预期的内容,但这取决于你.
干杯!
Mic*_*son 10
我认为troelskn很接近.我会这样做:
class ClassToTest
{
protected function testThisMethod()
{
// Implement stuff here
}
}
Run Code Online (Sandbox Code Playgroud)
然后,实现这样的事情:
class TestClassToTest extends ClassToTest
{
public function testThisMethod()
{
return parent::testThisMethod();
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您将针对TestClassToTest运行测试.
应该可以通过解析代码自动生成这样的扩展类.如果PHPUnit已经提供了这样的机制(虽然我还没有检查过),我不会感到惊讶.
您确实可以以通用方式使用__call()来访问受保护的方法。为了能够测试这堂课
class Example {
protected function getMessage() {
return 'hello';
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在ExampleTest.php中创建一个子类:
class ExampleExposed extends Example {
public function __call($method, array $args = array()) {
if (!method_exists($this, $method))
throw new BadMethodCallException("method '$method' does not exist");
return call_user_func_array(array($this, $method), $args);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,__call()方法不会以任何方式引用该类,因此您可以使用要测试的受保护方法为每个类复制以上内容,而只需更改类声明即可。您可能可以将此函数放在一个通用的基类中,但是我还没有尝试过。
现在,测试用例本身仅在构建要测试的对象方面有所不同,将ExampleExposed替换为Example。
class ExampleTest extends PHPUnit_Framework_TestCase {
function testGetMessage() {
$fixture = new ExampleExposed();
self::assertEquals('hello', $fixture->getMessage());
}
}
Run Code Online (Sandbox Code Playgroud)
我相信PHP 5.3允许您使用反射直接更改方法的可访问性,但是我认为您必须分别为每个方法进行更改。
我将把帽子戴在这里:
我曾经使用__call hack取得了不同程度的成功。我想到的替代方法是使用Visitor模式:
1:生成一个stdClass或自定义类(强制类型)
2:使用所需的方法和参数来填充
3:确保您的SUT具有acceptVisitor方法,该方法将使用访问类中指定的参数执行该方法
4:将其注入您要测试的班级
5:SUT将操作结果注入访问者
6:将测试条件应用于“访客”的结果属性
归档时间: |
|
查看次数: |
110762 次 |
最近记录: |