PHPUnit으로 보호 된 메소드를 테스트하는 모범 사례 [닫기]
개인 메소드 정보 를 테스트합니까? 에 대한 토론을 찾았습니다 .
일부 클래스에서는 보호 된 메소드를 갖고 싶지만 테스트하도록 결정했습니다. 이러한 방법 중 일부는 정적이고 짧습니다. 대부분의 공개 메소드는이 메소드를 사용하므로 나중에 테스트를 안전하게 제거 할 수 있습니다. 그러나 TDD 접근 방식으로 시작하고 디버깅을 피하기 위해 실제로 테스트하고 싶습니다.
나는 다음을 생각했다.
- 답변에 조언 된 방법 객체 는 이것에 대해 과도한 것으로 보입니다.
- 공개 방법으로 시작하고 더 높은 수준의 테스트로 코드 적용 범위를 지정하면 보호를 설정하고 테스트를 제거하십시오.
- 보호 가능한 메소드를 공개하는 테스트 가능한 인터페이스로 클래스 상속
가장 좋은 방법은 무엇입니까? 다른 것이 있습니까?
JUnit은 자동으로 보호 된 메소드를 공개로 변경하는 것으로 보이지만 자세히 살펴 보지는 않았습니다. PHP는 리플렉션을 통해 이것을 허용하지 않습니다 .
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(...));
...
}
당신은 이미 알고있는 것처럼 보이지만 어쨌든 나는 그것을 다시 언급 할 것입니다. 보호 된 방법을 테스트해야하는 경우 나쁜 신호입니다. 단위 테스트의 목적은 클래스의 인터페이스를 테스트하는 것이며 보호 된 메소드는 구현 세부 사항입니다. 그것은 말이되는 경우가 있습니다. 상속을 사용하면 수퍼 클래스가 서브 클래스에 대한 인터페이스를 제공하는 것으로 볼 수 있습니다. 따라서 여기서는 보호 된 방법을 테스트해야합니다 (그러나 절대 개인용은 아닙니다 ). 이에 대한 해결책은 테스트 목적으로 서브 클래스를 작성하고이를 사용하여 메소드를 노출시키는 것입니다. 예 :
class Foo {
protected function stuff() {
// secret stuff, you want to test
}
}
class SubFoo extends Foo {
public function exposedStuff() {
return $this->stuff();
}
}
상속을 항상 컴포지션으로 바꿀 수 있습니다. 코드를 테스트 할 때는 일반적으로이 패턴을 사용하는 코드를 다루기가 훨씬 쉬우므로 해당 옵션을 고려할 수 있습니다.
teastburn 이 올바른 접근 방식을 가지고 있습니다. 더 간단한 방법은 메소드를 직접 호출하고 답변을 반환하는 것입니다.
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);
}
}
테스트에서 간단히 다음과 같이 호출 할 수 있습니다.
$returnVal = PHPUnitUtil::callMethod(
$this->object,
'_nameOfProtectedMethod',
array($arg1, $arg2)
);
uckelman 's answer에 정의 된 getMethod ()에 약간의 변형을 제안하고 싶습니다 .
이 버전은 하드 코딩 된 값을 제거하고 사용법을 약간 단순화하여 getMethod ()를 변경합니다. 아래 예제와 같이 PHPUnitUtil 클래스에 추가하거나 PHPUnit_Framework_TestCase-extending 클래스 (또는 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
}
또한 예상되는 것을 명시하기 위해 getProtectedMethod ()라는 별칭 함수를 만들었지 만 그 기능은 사용자에게 달려 있습니다.
건배!
나는 troelskn이 가까이 있다고 생각합니다. 대신이 작업을 수행합니다.
class ClassToTest
{
protected testThisMethod()
{
// Implement stuff here
}
}
그런 다음 다음과 같이 구현하십시오.
class TestClassToTest extends ClassToTest
{
public testThisMethod()
{
return parent::testThisMethod();
}
}
그런 다음 TestClassToTest에 대해 테스트를 실행하십시오.
코드를 구문 분석하여 이러한 확장 클래스를 자동으로 생성 할 수 있어야합니다. PHPUnit이 이미 그러한 메커니즘을 제공한다고해도 놀라지 않을 것입니다 (확인하지는 않았지만).
모자를 고리에 넣을 것입니다.
나는 __call 핵을 다양한 수준의 성공으로 사용했습니다. 내가 생각해 낸 대안은 방문자 패턴을 사용하는 것입니다.
1 : stdClass 또는 사용자 정의 클래스를 생성하십시오 (유형을 시행하기 위해)
2 : 필수 메소드 및 인수로이를 준비하십시오.
3 : SUT에 방문 클래스에 지정된 인수로 메소드를 실행하는 acceptVisitor 메소드가 있는지 확인하십시오.
4 : 시험하고자하는 수업에 주입
5 : SUT는 조작 결과를 방문자에게 주입합니다.
6 : 테스트 조건을 방문자의 결과 속성에 적용
실제로 __call ()을 일반적인 방식으로 사용하여 보호 된 메소드에 액세스 할 수 있습니다. 이 수업을 테스트 할 수 있도록
class Example {
protected function getMessage() {
return 'hello';
}
}
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);
}
}
__call () 메서드는 어떤 식 으로든 클래스를 참조하지 않으므로 테스트하려는 보호 된 메서드를 사용하여 각 클래스에 대해 위의 내용을 복사하고 클래스 선언 만 변경할 수 있습니다. 이 함수를 공통 기본 클래스에 배치 할 수는 있지만 시도하지는 않았습니다.
이제 테스트 사례 자체는 테스트 할 개체를 구성하는 위치 만 다르고 ExampleExposed를 Example로 교체했습니다.
class ExampleTest extends PHPUnit_Framework_TestCase {
function testGetMessage() {
$fixture = new ExampleExposed();
self::assertEquals('hello', $fixture->getMessage());
}
}
PHP 5.3에서는 리플렉션을 사용하여 메소드의 접근성을 직접 변경할 수 있다고 생각하지만 각 메소드마다 개별적으로 변경해야한다고 가정합니다.
"Henrik Paul"의 해결 방법 / 아이디어에 대한 다음 해결 방법을 제안합니다. :)
클래스의 개인 메소드 이름을 알고 있습니다. 예를 들어 _add (), _edit (), _delete () 등과 같습니다.
따라서 단위 테스트 측면에서 테스트하려는 경우 __call () 메서드가 호출 될 때 (_addPhpunit () 메서드가 호출되지 않기 때문에 일반적인 단어 (예 : _addPhpunit) 를 접두사 및 / 또는 접미사로 사용하여 개인 메서드를 호출 하면됩니다. 소유자 클래스의 경우 __call () 메소드에 필요한 코드를 삽입하여 접두사 / 접미사 단어 (Phpunit)를 제거한 다음 거기에서 개인 메소드를 추론합니다. 이것은 마술 방법의 또 다른 좋은 사용법입니다.
사용해보십시오.
참고 URL : https://stackoverflow.com/questions/249664/best-practices-to-test-protected-methods-with-phpunit
'IT' 카테고리의 다른 글
하위 디렉토리에 하위 모듈을 추가하려면 어떻게합니까? (0) | 2020.03.26 |
---|---|
SQLAlchemy에서 filter와 filter_by의 차이점은 무엇입니까? (0) | 2020.03.26 |
MySQL에서 최대 두 값을 얻는 방법은 무엇입니까? (0) | 2020.03.26 |
time.struct_time 객체를 datetime 객체로 어떻게 변환합니까? (0) | 2020.03.26 |
두 개의 다른 디렉토리에 존재하는 Diff 파일 (0) | 2020.03.26 |