PHPUnit - başlıklarını göndermek için gereken öğeleri ile Birim Test

9 Cevap php

Ben şu anda, ancak ben şu anda Oturum Yöneticisi yazmaya çalışıyorum ve bunu yaparken sorunlar yaşıyorum, yazıyorum ne yanında testleri denemek ve geliştirmek için PHPUnit ile çalışıyorum ...

Oturum taşıma sınıfının yapıcısı

private function __construct()
{
	if (!headers_sent())
	{
		session_start();
		self::$session_id = session_id();
	}
}

Bu test başlamadan önce PHPUnit metin gönderir Ancak, bu nesne üzerinde herhangi bir test HTTP "Başlıklar" gönderilmiş gibi, başarısız bir testi verir ...

9 Cevap

Peki, oturum yöneticisi temelde tasarım tarafından bozuldu. Şey test edebilmek için, yan etkilerinden izole etmek mümkün olmalıdır. Ne yazık ki, PHP (echo, header, exit, session_start küresel devlet serbest kullanımını teşvik ki, böyle bir şekilde tasarlanmış vb gibi).

Yapabileceğiniz en iyi şey, zamanında takas edilebilir bir bileşeni yan etkileri, izole etmektir. Canlı kod gerçek yan etkileri var adaptörleri, kullanır iken Bu şekilde, testler alay nesneleri kullanabilirsiniz. Bu Ben kullandığınız tahmin tekiz, iyi oynamak olmadığını göreceksiniz. Yani kod dağıtılan paylaşılan nesneleri almak için bazı başka mekanizma kullanmak gerekecek. Statik bir kayıt defteri ile başlayabilirsiniz, ama öğrenme biraz sakıncası yoksa daha iyi çözümler vardır.

Bunu yapamıyorsanız, her zaman entegrasyon testleri yazma seçeneği var. Örn. ve phpunit en eşdeğerini kullanın WebTestCase .

phpunit testleri () hatta ilk testte doğru dönmek headers_sent neden böylece çalıştırmak gibi çıktı yazdırır.

Bu sorunu aşmak için bütün bir test paketi için sadece kurulum komut () ob_start kullanmanız gerekir.

Örneğin, phpunit tarafından yüklenen ilk şey AllTests.php adında bir dosya var diyelim. Bu komut aşağıdaki gibi görünebilir:

<?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;
    }
}

: Çağıran PHPUnit için bir önyükleme dosyası oluşturun

session_start();

Sonra bu gibi phpunit başlatın:

phpunit --bootstrap pathToBootstrap.php --anotherSwitch /your/test/path/

Önyükleme dosyası her şeyden önce çağrılır, böylece başlık gönderilmedi ve her şey iyi çalışması gerekir.

Ben, "doğru" çözüm olduğunu PHP'nin oturum ilişkin işlevler için bir sarıcı (yani basit bunun test edilmesi gerekmez) çok basit bir sınıf oluşturmak için olduğunu düşünüyorum, ve bunun yerine session_start() çağrı kullanabilirsiniz vb doğrudan.

Testte davalarını nesne yerine gerçek bir durum bilgisi olan, untestable oturum sınıf geçmek.

private function __construct(SessionWrapper $wrapper)
{
   if (!$wrapper->headers_sent())
   {
      $wrapper->session_start();
      $this->session_id = $wrapper->session_id();
   }
}

As I'm unittesting my bootstrap right now (yes I know most of you don't do that), I'm running in to the same problem (both header() and session_start()). The solution I found is rather simple, in your unittest bootstrap define a constant and simply check it before sending the header or starting the session:

// phpunit_bootstrap.php
define('UNITTEST_RUNNING', true);

// bootstrap.php (application bootstrap)
defined('UNITTEST_RUNNING') || define('UNITTEST_RUNNING', false);
.....
if(UNITTEST_RUNNING===false){
    session_start();
}

Ben bu tasarım ile mükemmel olmadığını kabul ediyorum, ama arzu edilmez, büyük parçaları yeniden, varolan bir uygulama unittesting ediyorum. Ben de __ çağrısı () ve __ set () sihirli yöntemler kullanılarak özel yöntemleri sınamak için aynı mantığı kullanarak.

public function __set($name, $value){
    if(UNITTEST_RUNNING===true){
       $name='_' . $name;
       $this->$name=$value;
    }
    throw new Exception('__set() can only be used when unittesting!');
 }

Eğer teste başlamadan önce çıktı tamponlama kullanıyorsanız değil miyim? Eğer çıkış şeyi tampon varsa, hiçbir çıkış olarak üstbilgileri ayarlayarak sorunlar henüz o noktada müşteriye gönderilen olurdu olmamalıdır.

OB, sınıfları içinde bir yerde kullanılsa bile, bu istiflenebilir ve OB içeride neler etkilememelidir.

Bildiğim kadarıyla Zend Framework kendi Zend_Session paket testler için aynı çıkış arabelleği kullanır. Eğer Başlamak için kendi test durumlarda bir göz atabilirsiniz.

Önyükleme dosyasının oluşturulması, 4 ileti geri Bu etrafında temiz yol gibi görünüyor dikkat çekti.

Often with PHP we are having to maintain, and try to add some kind of engineering discipline to legacy projects that are abysmally put together. We don't have the time (or the authority) to ditch the whole pile of rubbish and start again, so the first anwer by troelskn isn't always possible as a way forward. ( If we could go back to the initial design, then we could ditch PHP altogether and use something more modern, such as ruby or python, rather than help perpetuate this COBOL of the web development world. )

Bir boostrap dosyasında oturumu başlatmadan daha da yuvarlak bu konuları alır, onları boyunca session_start veya setcookie kullanmak modülleri için birim testleri yazmak için çalışıyorsanız.

Ben insanlar aslında bu kolayca yapılabilir olmadığını yayınlanmıştır inanamıyorum!

Benim kişisel kod galeri projelerinden biri bazı kodunu kopyalamak için gidiyorum ve daha fazla öğrenmek, böylece ben tüm bağlantı yapıştırmak için gidiyorum.

class MyTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        parent::setUp();

        ob_start(); // <-- very important!
    }

    protected function tearDown()
    {
        header_remove(); // <-- VERY important.
        parent::tearDown();
    }

    public function testProperHeaderSet()
    {
        header('Location: foo');
        $headers_list = headers_list();
        $this->assertNotEmpty($headers_list);
        $this->assertContains('Location: foo', $headers_list);
    }
}

Here's the link to the full unit test.