정적 메소드 호출을 모방하기 위해 phpunit의 모의 개체?
문제
데이터베이스에서 데이터 액세스를 관리하는 클래스를 테스트하려고합니다 (본질적으로 CRUD). 우리가 사용하고있는 DB 라이브러리에는 API가 있는데, 여기서 먼저 정적 호출로 테이블 객체를 얻습니다.
function getFoo($id) {
$MyTableRepresentation = DB_DataObject::factory("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
... 당신은 아이디어를 얻습니다.
우리는이 방법을 테스트하려고하지만 (a) 테스트를 위해 실제 DB 연결이 필요하지 않도록 데이터 객체 항목을 조롱하고 (b) 테스트를 위해 DB_DATAOBJECT LIB를 포함 할 필요조차 없습니다. .
그러나 PhPunit에서는 정적 호출을 적절하게 설정하기 위해 $ this-> getMock ()를 얻을 수없는 것 같습니다. 나는 가지고있다...
$DB_DataObject = $this->getMock('DB_DataObject', array('factory'));
... 그러나 테스트는 여전히 알려지지 않은 방법 "공장"이라고 말합니다. DB_DataObject를 찾을 수 없기 때문에 객체를 만들고 있다는 것을 알고 있습니다. 이제 가능합니다. 그러나 방법이 없습니까?
내가 정말로하고 싶은 것은 두 개의 모의 개체를 갖는 것입니다. 하나는 테이블 객체를위한 것입니다. 따라서 공장이 정적 호출임을 지정해야 할뿐만 아니라 이미 설정 한 다른 Mock 객체를 반환해야합니다.
나는 얼마 전에 단순하게 이것을했다고 경고로 언급해야하며 (코드를 찾을 수 없음) 잘 작동했습니다.
무엇을 제공합니까?
업데이트
나는 그것이 exposts ()와 관련이 있다는 것을 이해하기 시작했습니다.
해결책
나는 정적 호출을 사용하지 않는 것이 더 낫다는 것에 동의합니다. 그러나 DB_DataObject가 타사 라이브러리이고 정적 호출은 그들의 우리가 아닌 코드 사용에 대한 모범 사례. 반환 된 객체를 직접 구성하는 것과 관련된 객체를 사용하는 다른 방법이 있습니다. 그것은 단지 DB_DO 클래스를 사용하는 클래스 파일에 어떤 클래스 파일을 사용하는지 명령문을 포함시킵니다. 테스트에서 같은 이름의 클래스를 조롱하려고한다면 테스트가 깨지거나 격리되지 않기 때문에 짜증납니다.
다른 팁
라이브러리를 변경할 수 없으면 액세스를 변경하십시오. 코드의 인스턴스 메소드로 db_dataobject :: factory ()에 대한 모든 호출을 Refactor :
function getFoo($id) {
$MyTableRepresentation = $this->getTable("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
function getTable($table) {
return DB_DataObject::factory($table);
}
이제 테스트중인 클래스의 부분 모의를 사용할 수 있으며 GetTable ()가 모의 테이블 개체를 반환 할 수 있습니다.
function testMyTable() {
$dao = $this->getMock('MyTableDao', array('getMock'));
$table = $this->getMock('DB_DataObject', ...);
$dao->expects($this->any())
->method('getTable')
->with('mytable')
->will($this->returnValue($table));
$table->expects...
...test...
}
이것은 코드의 종속성의 좋은 예입니다. 디자인으로 인해 실제 클래스가 아닌 모의를 주입하는 것은 불가능했습니다.
첫 번째 제안은 정적 호출이 아닌 인스턴스를 사용하도록 코드를 리팩터링하는 것입니다.
DB_DataObject 클래스에서 누락 된 것은 공장 메소드를 호출하기 전에 준비된 DB 객체를 전달하는 세터입니다. 이렇게하면 필요가 발생하면 모의 또는 맞춤형 DB 객체 (동일한 인터페이스 포함)를 전달할 수 있습니다.
테스트 설정에서 :
public function setUp() {
$mockDb = new MockDb();
DB_DataObject::setAdapter($mockDb);
}
Factory () 메소드는 조롱 된 DB 인스턴스를 반환해야합니다. 아직 수업에 통합되지 않은 경우 공장 () 메소드를 리팩터링하고 작동하도록해야 할 것입니다.
테스트 케이스에서 DB_DATAOBJECT의 클래스 파일을 포함시켜야합니까? PHPUNIT가 개체를 조롱하려고 시도하기 전에 클래스가 존재하지 않으면 이와 같은 오류를 얻을 수 있습니다.
phpunit mockfunction extension plus runkit을 사용하면 정적 메소드를 조롱 할 수도 있습니다. 원숭이 패치이기 때문에 조심해야하므로 극한의 경우에만 사용해야합니다. 좋은 프로그래밍 관행을 대체하지 않습니다.