Вымысел конкретного метода в абстрактном классе с использованием phpunit
Есть ли какие-либо хорошие способы издеваться над конкретными методами в абстрактных классах с использованием PHPUnit?
Что я нашел до сих пор:
- expects() → will() отлично работает с абстрактными методами
- Это не работает для конкретных методов. Вместо этого выполняется исходный метод.
- Использование mockbuilder и предоставление всех абстрактных методов и конкретного метода setMethods() работает. Однако для этого требуется указать все абстрактные методы, сделав тест хрупким и слишком подробным.
- MockBuilder:: getMockForAbstractClass() игнорирует setMethod().
Ниже приведены некоторые модульные тесты, в которых рассмотрены следующие пункты:
abstract class AbstractClass {
public function concreteMethod() {
return $this->abstractMethod();
}
public abstract function abstractMethod();
}
class AbstractClassTest extends PHPUnit_Framework_TestCase {
/**
* This works for abstract methods.
*/
public function testAbstractMethod() {
$stub = $this->getMockForAbstractClass('AbstractClass');
$stub->expects($this->any())
->method('abstractMethod')
->will($this->returnValue(2));
$this->assertSame(2, $stub->concreteMethod()); // Succeeds
}
/**
* Ideally, I would like this to work for concrete methods too.
*/
public function testConcreteMethod() {
$stub = $this->getMockForAbstractClass('AbstractClass');
$stub->expects($this->any())
->method('concreteMethod')
->will($this->returnValue(2));
$this->assertSame(2, $stub->concreteMethod()); // Fails, concreteMethod returns NULL
}
/**
* One way to mock the concrete method, is to use the mock builder,
* and set the methods to mock.
*
* The downside of doing it this way, is that all abstract methods
* must be specified in the setMethods() call. If you add a new abstract
* method, all your existing unit tests will fail.
*/
public function testConcreteMethod__mockBuilder_getMock() {
$stub = $this->getMockBuilder('AbstractClass')
->setMethods(array('concreteMethod', 'abstractMethod'))
->getMock();
$stub->expects($this->any())
->method('concreteMethod')
->will($this->returnValue(2));
$this->assertSame(2, $stub->concreteMethod()); // Succeeds
}
/**
* Similar to above, but using getMockForAbstractClass().
* Apparently, setMethods() is ignored by getMockForAbstractClass()
*/
public function testConcreteMethod__mockBuilder_getMockForAbstractClass() {
$stub = $this->getMockBuilder('AbstractClass')
->setMethods(array('concreteMethod'))
->getMockForAbstractClass();
$stub->expects($this->any())
->method('concreteMethod')
->will($this->returnValue(2));
$this->assertSame(2, $stub->concreteMethod()); // Fails, concreteMethod returns NULL
}
}
Ответы
Ответ 1
Я переопределяю getMock()
в своем базовом тестовом примере, чтобы добавить все абстрактные методы, потому что вы все равно должны их издеваться над ними. Вы можете сделать что-то подобное с строителем, без сомнения.
Важно: Вы не можете макетировать частные методы.
public function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE) {
if ($methods !== null) {
$methods = array_unique(array_merge($methods,
self::getAbstractMethods($originalClassName, $callAutoload)));
}
return parent::getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload);
}
/**
* Returns an array containing the names of the abstract methods in <code>$class</code>.
*
* @param string $class name of the class
* @return array zero or more abstract methods names
*/
public static function getAbstractMethods($class, $autoload=true) {
$methods = array();
if (class_exists($class, $autoload) || interface_exists($class, $autoload)) {
$reflector = new ReflectionClass($class);
foreach ($reflector->getMethods() as $method) {
if ($method->isAbstract()) {
$methods[] = $method->getName();
}
}
}
return $methods;
}
Ответ 2
Для этого был запрос Pull 2 года назад, но информация не была добавлена в документацию: https://github.com/sebastianbergmann/phpunit-mock-objects/pull/49
Вы можете передать свой конкретный метод в массиве в аргументе 7 getMockForAbstractClass().
Смотрите код: https://github.com/andreaswolf/phpunit-mock-objects/blob/30ee7452caaa09c46421379861b4128ef7d95e2f/PHPUnit/Framework/MockObject/Generator.php#L225