Ton*_*ony 66 php phpunit mocking
我有一个关于使用PHPUnit模拟类中的私有方法的问题.让我举一个例子:
class A {
public function b() {
// some code
$this->c();
// some more code
}
private function c(){
// some code
}
}
Run Code Online (Sandbox Code Playgroud)
如何将私有方法的结果存根以测试公共函数的更多代码部分.
解决了部分阅读在这里
edo*_*ian 81
通常,您不会直接测试或模拟私有和受保护的方法.
您要测试的是您班级的公共 API.其他所有内容都是您班级的实施细节,如果您更改了测试,则不应"破坏"您的测试.
当您发现"无法获得100%的代码覆盖率"时,这也会对您有所帮助,因为您的类中可能存在无法通过调用公共API执行的代码.
但如果你的班级看起来像这样:
class a {
public function b() {
return 5 + $this->c();
}
private function c() {
return mt_rand(1,3);
}
}
Run Code Online (Sandbox Code Playgroud)
我可以看到需要模拟c(),因为"随机"函数是全局状态,你无法测试.
"干净?/详细?/过于复杂 - 可能?/我喜欢它通常"的解决方案
class a {
public function __construct(RandomGenerator $foo) {
$this->foo = $foo;
}
public function b() {
return 5 + $this->c();
}
private function c() {
return $this->foo->rand(1,3);
}
}
Run Code Online (Sandbox Code Playgroud)
现在不再需要模拟"c()"了,因为它不包含任何全局变量,你可以很好地测试.
如果你不想做或不能从你的私有函数中删除全局状态(糟糕的事情,那么糟糕的事实或者你的定义可能会有所不同),你可以对模拟进行测试.
// maybe set the function protected for this to work
$testMe = $this->getMock("a", array("c"));
$testMe->expects($this->once())->method("c")->will($this->returnValue(123123));
Run Code Online (Sandbox Code Playgroud)
并且针对这个模拟运行测试,因为你取出/ mock的唯一函数是"c()".
引用"实用单元测试"一书:
"一般来说,你不想为了测试而破坏任何封装(或者像妈妈常说的那样,"不要暴露你的私人!").大多数时候,你应该能够测试一个类通过行使其公共方法.如果隐藏在私人或受保护访问背后的重要功能,那可能是一个警告信号,表明还有另一个班级在努力逃脱."
多一点: Why you don't want test private methods.
doc*_*ore 25
您可以测试私有方法, 但不能模拟(模拟)此方法的运行.
此外,反射不允许您将私有方法转换为受保护或公共方法.setAccessible只允许您调用原始方法.
或者,您可以使用runkit重命名私有方法并包含"新实现".但是,这些功能是实验性的,不建议使用它们.
Dav*_*ess 24
您可以使用反射并setAccessible()在测试中允许您以这样的方式设置对象的内部状态,即它将从私有方法返回您想要的内容.你需要使用PHP 5.3.2.
$fixture = new MyClass(...);
$reflector = new ReflectionProperty('MyClass', 'myPrivateProperty');
$reflector->setAccessible(true);
$reflector->setValue($fixture, 'value');
// test $fixture ...
Run Code Online (Sandbox Code Playgroud)
小智 13
你可以模拟受保护的方法,所以如果你可以将C转换为protected,那么这段代码将有所帮助.
$mock = $this->getMockBuilder('A')
->disableOriginalConstructor()
->setMethods(array('C'))
->getMock();
$response = $mock->B();
Run Code Online (Sandbox Code Playgroud)
这肯定会奏效,它对我有用.然后,为了覆盖受保护的方法C,您可以使用反射类.
Eds*_*ina 11
假设您需要测试$ myClass-> privateMethodX($ arg1,$ arg2),您可以使用反射执行此操作:
$class = new ReflectionClass ($myClass);
$method = $class->getMethod ('privateMethodX');
$method->setAccessible(true);
$output = $method->invoke ($myClass, $arg1, $arg2);
Run Code Online (Sandbox Code Playgroud)
小智 10
以下是其他答案的变体,可用于将此类调用排成一行:
public function callPrivateMethod($object, $methodName)
{
$reflectionClass = new \ReflectionClass($object);
$reflectionMethod = $reflectionClass->getMethod($methodName);
$reflectionMethod->setAccessible(true);
$params = array_slice(func_get_args(), 2); //get all the parameters after $methodName
return $reflectionMethod->invokeArgs($object, $params);
}
Run Code Online (Sandbox Code Playgroud)
我为我的案例设计了这个通用类:
/**
* @author Torge Kummerow
*/
class Liberator {
private $originalObject;
private $class;
public function __construct($originalObject) {
$this->originalObject = $originalObject;
$this->class = new ReflectionClass($originalObject);
}
public function __get($name) {
$property = $this->class->getProperty($name);
$property->setAccessible(true);
return $property->getValue($this->originalObject);
}
public function __set($name, $value) {
$property = $this->class->getProperty($name);
$property->setAccessible(true);
$property->setValue($this->originalObject, $value);
}
public function __call($name, $args) {
$method = $this->class->getMethod($name);
$method->setAccessible(true);
return $method->invokeArgs($this->originalObject, $args);
}
}
Run Code Online (Sandbox Code Playgroud)
使用此类,您现在可以轻松透明地释放任何对象上的所有私有函数/字段。
$myObject = new Liberator(new MyObject());
/* @var $myObject MyObject */ //Usefull for code completion in some IDEs
//Writing to a private field
$myObject->somePrivateField = "testData";
//Reading a private field
echo $myObject->somePrivateField;
//calling a private function
$result = $myObject->somePrivateFunction($arg1, $arg2);
Run Code Online (Sandbox Code Playgroud)
如果性能很重要,则可以通过缓存Liberator类中调用的属性/方法来提高性能。
一种选择是c() protected代替,private然后子类和覆盖c().然后用你的子类测试.另一个选择是重构c()为可以注入的不同类A(这称为依赖注入).然后c()在单元测试中注入一个带有模拟实现的测试实例.
| 归档时间: |
|
| 查看次数: |
70579 次 |
| 最近记录: |