헤더를 보내야 하는 항목을 사용한 단위 테스트
-
06-07-2019 - |
문제
저는 현재 제가 작성하고 있는 것과 함께 테스트를 개발하기 위해 PHPUnit을 사용하고 있습니다. 그러나 현재 Session Manager를 작성하는 중인데 문제가 있습니다...
세션 처리 클래스의 생성자는 다음과 같습니다.
private function __construct()
{
if (!headers_sent())
{
session_start();
self::$session_id = session_id();
}
}
그러나 PHPUnit은 테스트를 시작하기 전에 텍스트를 전송하므로 이 객체에 대한 모든 테스트는 HTTP "헤더"가 전송되었기 때문에 실패한 테스트를 반환합니다.
해결책
글쎄, 세션 관리자는 기본적으로 설계 상 손상되었습니다.무언가를 테스트하려면 부작용으로부터 격리할 수 있어야 합니다.불행하게도 PHP는 전역 상태(echo
, header
, exit
, session_start
등.등.).
당신이 할 수 있는 가장 좋은 방법은 런타임에 교체될 수 있는 구성 요소의 부작용을 격리하는 것입니다.이렇게 하면 테스트에서는 모의 객체를 사용할 수 있고 라이브 코드에서는 실제 부작용이 있는 어댑터를 사용할 수 있습니다.당신은 이것이 당신이 사용하고 있다고 생각하는 싱글톤에서는 잘 작동하지 않는다는 것을 알게 될 것입니다.따라서 공유 객체를 코드에 배포하려면 다른 메커니즘을 사용해야 합니다.정적 레지스트리로 시작할 수 있지만 약간의 학습에 신경 쓰지 않는다면 더 나은 솔루션이 있습니다.
그렇게 할 수 없다면 항상 통합 테스트를 작성할 수 있는 옵션이 있습니다.예.PHPUnit과 동등한 것을 사용하십시오 WebTestCase
.
다른 팁
PhPunit 용 부트 스트랩 파일을 작성하여 호출하십시오.
session_start();
그런 다음 다음과 같이 phpunit을 시작하십시오.
phpunit --bootstrap pathToBootstrap.php --anotherSwitch /your/test/path/
부트 스트랩 파일은 다른 모든 것보다 먼저 호출되므로 헤더가 전송되지 않고 모든 것이 잘 작동해야합니다.
PhPunit은 테스트가 실행될 때 출력을 인쇄하여 Headers_Sent ()가 첫 번째 테스트에서도 True를 반환합니다.
전체 테스트 스위트의 경우이 문제를 극복하려면 설정 스크립트에서 OB_START ()를 사용하면됩니다.
예를 들어, phpunit에서 가장 먼저로드 한 AllTests.php라는 파일이 있다고 가정 해 봅시다. 해당 스크립트는 다음과 같을 수 있습니다.
<?php
ob_start();
require_once 'YourFramework/AllTests.php';
class AllTests {
public static function suite() {
$suite = new PHPUnit_Framework_TestSuite('YourFramework');
$suite->addTest(YourFramework_AllTests::suite());
return $suite;
}
}
나는 같은 문제를 겪었고 다음과 같이 -stderr 플래그로 phpunit을 호출하여 해결했습니다.
phpunit --stderr /path/to/your/test
누군가를 돕기를 바랍니다!
"올바른"솔루션은 PHP의 세션 관련 기능을위한 래퍼 인 매우 간단한 클래스 (테스트 할 필요가 없음)를 만드는 것입니다. session_start()
, 직접.
테스트 패스 모의 객체에서 실제 상태가없는 테스트 할 수없는 세션 클래스 대신.
private function __construct(SessionWrapper $wrapper)
{
if (!$wrapper->headers_sent())
{
$wrapper->session_start();
$this->session_id = $wrapper->session_id();
}
}
아무도 XDEBUG 옵션을 나열하지 않은 이유가 궁금합니다.
/**
* @runInSeparateProcess
* @requires extension xdebug
*/
public function testGivenHeaderIsIncludedIntoResponse()
{
$customHeaderName = 'foo';
$customHeaderValue = 'bar';
// Here execute the code which is supposed to set headers
// ...
$expectedHeader = $customHeaderName . ': ' . $customHeaderValue;
$headers = xdebug_get_headers();
$this->assertContains($expectedHeader, $headers);
}
테스트를 시작하기 전에 출력 버퍼링을 사용할 수 없습니까? 출력되는 모든 것을 버퍼링하면 해당 시점에서 출력이 아직 클라이언트에 전송되지 않았기 때문에 헤더를 설정하는 데 문제가 없습니다.
OB가 클래스 내부의 어딘가에 사용 되더라도 쌓을 수 있으며 OB는 내부에서 일어나는 일에 영향을 미치지 않아야합니다.
내가 아는 한 Zend Framework는 Zend_session 패키지 테스트에 동일한 출력 버퍼링을 사용합니다. 테스트 사례를 살펴보고 시작할 수 있습니다.
4개의 게시물을 다시 지적한 부트스트랩 파일을 생성하는 것이 이 문제를 해결하는 가장 깔끔한 방법인 것 같습니다.
종종 우리는 PHP를 사용하여 엄청나게 결합된 레거시 프로젝트에 일종의 엔지니어링 원칙을 유지하고 추가하려고 노력해야 합니다.우리에게는 쓰레기 더미 전체를 버리고 다시 시작할 시간(또는 권한)이 없으므로 앞으로 나아갈 길로서 troelskn의 첫 번째 답변이 항상 가능한 것은 아닙니다.(초기 디자인으로 돌아갈 수 있다면 웹 개발 세계의 COBOL을 영속시키는 데 도움이 되기보다는 PHP를 완전히 버리고 Ruby나 Python과 같은 보다 현대적인 것을 사용할 수 있습니다.)
session_start 또는 setcookie를 사용하는 모듈에 대한 단위 테스트를 작성하려는 경우 부스트랩 파일에서 세션을 시작하면 이러한 문제가 발생합니다.
지금 내 부트 스트랩을 단위로 테스트하고 있기 때문에 (예, 대부분의 사람들이 그렇게하지 않는다는 것을 알고 있습니다), 나는 같은 문제 (Header () 및 session_start ())에 실행됩니다. 내가 찾은 솔루션은 다소 간단합니다. 단위 테스트 부트 스트랩에서 상수를 정의하고 헤더를 보내거나 세션을 시작하기 전에 간단히 확인합니다.
// phpunit_bootstrap.php
define('UNITTEST_RUNNING', true);
// bootstrap.php (application bootstrap)
defined('UNITTEST_RUNNING') || define('UNITTEST_RUNNING', false);
.....
if(UNITTEST_RUNNING===false){
session_start();
}
나는 이것이 디자인에 의해 완벽하지 않다는 데 동의하지만, 기존 응용 프로그램을 단위로 테스트하고 있으며, 큰 부품을 다시 작성하는 것이 바람직하지 않습니다. 또한 동일한 논리를 사용하여 __call () 및 __set () 마법 메소드를 사용하여 개인 메소드를 테스트합니다.
public function __set($name, $value){
if(UNITTEST_RUNNING===true){
$name='_' . $name;
$this->$name=$value;
}
throw new Exception('__set() can only be used when unittesting!');
}
코드를 테스트 할 수 있도록 세션을 주입 해야하는 것 같습니다. 내가 사용한 가장 좋은 옵션은 AURA.AUTH 인증 프로세스를위한 AURA.AUTH입니다.
Aura 프레임 워크는 아름답게 작성되었으며 다른 Aura 프레임 워크 종속성없이 자체적으로 Aura.auth를 사용할 수 있습니다.