Kanca geri çağırma testi


34

TDD kullanarak bir eklenti geliştiriyorum ve tamamen test edemediğim bir şey ... kancalar.

Tamam, kanca geri aramasını test edebilirim, ancak bir kancanın gerçekten tetiklenip tetiklenmediğini (hem özel kancalar hem de WordPress varsayılan kancalar) nasıl test edebilirim? Bazı alaycıların yardımcı olacağını farz ediyorum, ama ne kaçırdığımı çözemiyorum.

Test paketini WP-CLI ile kurdum. Bu cevaba göre , initkanca tetiklemeli, ama ... öyle değil; Ayrıca, kod WordPress içinde çalışır.

Anlayışıma göre, önyükleme şeridi en son yüklendi, bu yüzden init'i tetiklememek mantıklı geliyor, bu yüzden geriye kalan soru şudur: Kancalar tetiklenirse nasıl test etmeliyim?

Teşekkürler!

Önyükleme dosyası şöyle görünür:

$_tests_dir = getenv('WP_TESTS_DIR');
if ( !$_tests_dir ) $_tests_dir = '/tmp/wordpress-tests-lib';

require_once $_tests_dir . '/includes/functions.php';

function _manually_load_plugin() {
  require dirname( __FILE__ ) . '/../includes/RegisterCustomPostType.php';
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );

require $_tests_dir . '/includes/bootstrap.php';

test edilmiş dosya şöyle görünür:

class RegisterCustomPostType {
  function __construct()
  {
    add_action( 'init', array( $this, 'register_post_type' ) );
  }

  public function register_post_type()
  {
    register_post_type( 'foo' );
  }
}

Ve testin kendisi:

class CustomPostTypes extends WP_UnitTestCase {
  function test_custom_post_type_creation()
  {
    $this->assertTrue( post_type_exists( 'foo' ) );
  }
}

Teşekkürler!


Eğer koşuyorsanız phpunit, başarısız veya başarılı testleri görebiliyor musunuz? Yüklediniz bin/install-wp-tests.shmi
Sven

Bence sorunun bir kısmı RegisterCustomPostType::__construct(), eklenti testler için yüklendiğinde belki de asla çağrılmamasıdır. Ayrıca # 29827 numaralı hatadan etkilenmeniz de mümkündür ; belki de WP ünite test setini güncellemeyi deneyebilirsin.
JD

@Sven: evet, testler başarısız; i yüklü bin/install-wp-tests.sh@JD (i wp-cli kullanılan beri): yapı :: __ RegisterCustomPostType edilir denilen (sadece bir ilave die()açıklama ve PHPUnit orada durur)
Ionut Staicu

Birim sınama tarafında (benim forte'mdan değil) emin değilim, ancak kelimenin tam anlamıyla did_action()eylemlerin başlatılıp başlatılmadığını kontrol etmek için kullanabilirsiniz .
Rarst

@Rarst: Öneri için teşekkürler, ama yine de çalışmıyor. Nedense, zamanlamanın yanlış olduğunu düşünüyorum (testler initkancadan önce yapıldı ).
Ionut Staicu

Yanıtlar:


72

İzolasyonda test

Bir eklenti geliştirirken, en iyi yolu olduğunu test etmek olmadan WordPress ortamı yükleniyor.

WordPress olmadan kolayca test edilebilecek bir kod yazarsanız, kodunuz daha iyi hale gelir .

Ünite testinden geçirilen her bileşen, yalıtılmış bir şekilde test edilmelidir : bir sınıfı test ederken, diğer tüm kodların mükemmel çalıştığını varsayarak, yalnızca o belirli sınıfı test etmeniz gerekir.

İzolatör

Birim testlerinin "birim" olarak adlandırılmasının nedeni budur.

Ek bir avantaj olarak, çekirdek yüklemeden, testiniz çok daha hızlı çalışacaktır.

Yapıcıdaki kancalardan kaçının

Sana verebileceğim bir ipucu, yapıcılara kanca takmamaktır. Bu, kodunuzu yalıtılmış bir şekilde test edilebilir hale getirecek şeylerden biri.

OP'de test kodunu görelim:

class CustomPostTypes extends WP_UnitTestCase {
  function test_custom_post_type_creation() {
    $this->assertTrue( post_type_exists( 'foo' ) );
  }
}

Ve bu testin başarısız olduğunu varsayalım . Suçlu kim ?

  • Kanca hiç eklenmemiş ya da uygun değil mi?
  • yazı türünü kaydeden yöntem hiç veya yanlış argümanlarla çağrılmadı mı?
  • WordPress'te bir hata var mı?

Nasıl geliştirilebilir?

Sınıf kodunuzun olduğunu varsayalım:

class RegisterCustomPostType {

  function init() {
    add_action( 'init', array( $this, 'register_post_type' ) );
  }

  public function register_post_type() {
    register_post_type( 'foo' );
  }
}

(Not: Cevabın geri kalanında sınıfın bu versiyonuna değineceğim)

Bu sınıfı yazmamın yolu, çağırmadan sınıfın örneklerini oluşturmanıza olanak sağlar add_action.

Yukarıdaki sınıfta test edilecek 2 şey vardır:

  • yöntem init aslındaadd_action kendisine uygun argümanları iletmeyi çağırıyor
  • yöntem register_post_type aslındaregister_post_type işlevi çağırır

Yazı tipinin olup olmadığını kontrol etmeniz gerektiğini söylemedim: uygun eylemi eklerseniz ve ararsanız register_post_type, özel yazı tipi mevcut olmalıdır : yoksa, bu bir WordPress problemidir.

Unutmayın: Eklentinizi test ederken , WordPress kodunu değil , kodunuzu test etmeniz gerekir . Testlerinizde WordPress'in (kullandığınız diğer harici kütüphaneler gibi) iyi çalıştığını varsaymanız gerekir. Birim testinin anlamı budur .

Ama ... pratikte?

WordPress yüklü değilse, yukarıdaki sınıf yöntemlerini çağırmaya çalışırsanız, önemli bir hata alırsınız, bu nedenle işlevleri alay etmeniz gerekir.

"Manuel" yöntemi

Alaycı kütüphanenizi yazabildiğinizden emin olun ya da her yöntemi alay ederek "el ile". Mümkün. Bunu nasıl yapacağınızı söyleyeceğim, ama sonra size daha kolay bir yöntem göstereceğim.

Testler çalışırken WordPress yüklü değilse, işlevlerini, örneğin add_actionveya register_post_type.

Sahip olduğunuz önyükleme dosyanızdan yüklenen bir dosyanız olduğunu varsayalım:

function add_action() {
  global $counter;
  if ( ! isset($counter['add_action']) ) {
    $counter['add_action'] = array();
  }
  $counter['add_action'][] = func_get_args();
}

function register_post_type() {
  global $counter;
  if ( ! isset($counter['register_post_type']) ) {
    $counter['register_post_type'] = array();
  }
  $counter['register_post_type'][] = func_get_args();
}

Her çağrıldığında basit bir şekilde global bir diziye bir öğe eklemek için işlevleri yeniden yazdım.

Şimdi (zaten bir tane yoksa) kendi temel sınama durumu sınıfını genişletmeniz gerekir PHPUnit_Framework_TestCase: bu, sınamalarınızı kolayca yapılandırmanıza olanak tanır.

Gibi bir şey olabilir:

class Custom_TestCase extends \PHPUnit_Framework_TestCase {

    public function setUp() {
        $GLOBALS['counter'] = array();
    }

}

Bu şekilde, her testten önce, global sayaç sıfırlanır.

Ve şimdi test kodunuz ( yukarıda yayınlanan yeniden yazılmış sınıfa atıfta bulunuyorum ):

class CustomPostTypes extends Custom_TestCase {

  function test_init() {
     global $counter;
     $r = new RegisterCustomPostType;
     $r->init();
     $this->assertSame(
       $counter['add_action'][0],
       array( 'init', array( $r, 'register_post_type' ) )
     );
  }

  function test_register_post_type() {
     global $counter;
     $r = new RegisterCustomPostType;
     $r->register_post_type();
     $this->assertSame( $counter['register_post_type'][0], array( 'foo' ) );
  }

}

Dikkat etmelisin:

  • İki yöntemi ayrı ayrı arayabildim ve WordPress hiç yüklenmedi. Bu şekilde bir test başarısız olursa, suçlunun tam olarak kim olduğunu biliyorum .
  • Dediğim gibi, burada sınıfların beklenen argümanlarla WP işlevlerini çağırdığını test ediyorum. CPT'nin gerçekten var olup olmadığını test etmeye gerek yoktur. CPT'nin varlığını test ediyorsanız, eklenti davranışınızı değil, WordPress davranışını test ediyorsunuz ...

Güzel .. ama bu bir PITA!

Evet, tüm WordPress işlevlerini elle alay etmek zorunda kalırsanız, bu gerçekten bir acıdır. Verebileceğim bazı genel tavsiyeler, mümkün olduğu kadar az WP işlevi kullanmaktır: WordPress'i yeniden yazmak zorunda değilsiniz , ancak özel sınıflarda kullandığınız soyut WP işlevlerini alay edip kolayca sınanabilmeleri için.

Örneğin, yukarıdaki örneğe ilişkin olarak, yazı tiplerini kaydeden bir sınıf yazabilir register_post_typeve verilen argümanlarla 'init'i çağırırsınız. Bu soyutlama ile hala o sınıfı denemeniz gerekir, ancak kodunuzun yazı türlerini kaydettirdiği diğer yerlerde, o sınıfı kullanıp testlerde alay edersiniz.

Müthiş şey, eğer CPT kaydını iptal eden bir sınıf yazarsanız, bunun için ayrı bir depo oluşturabilirsiniz ve Composer gibi modern araçlar sayesinde, ihtiyaç duyduğunuz tüm projelere gömün: bir kez test edin, her yerde kullanın . Ve içinde bir hata bulursanız, tek bir yerde düzeltebilirsiniz ve basit bir şekilde kullanıldığı composer updatetüm projeler de sabitlenir.

İkinci kez: Yalıtımda test edilebilir bir kod yazmak, daha iyi kod yazmak anlamına gelir.

Ama er ya da geç WP işlevlerini bir yerde kullanmam gerekiyor ...

Tabii ki. Asla çekirdeğe paralel davranmamalısın , hiç mantıklı değil. WP işlevlerini saran sınıflar yazabilirsiniz, ancak bu sınıfların da test edilmesi gerekir. Yukarıda açıklanan "manuel" yöntem çok basit işler için kullanılabilir, ancak bir sınıf çok WP işlevi içerdiğinde bu bir acı olabilir.

Neyse ki, orada iyi şeyler yazan iyi insanlar var. En büyük WP ajanslarından biri olan 10up , eklentileri doğru şekilde test etmek isteyen insanlar için çok büyük bir kütüphane barındırıyor. Öyle WP_Mock.

WP'nin kancalı fonksiyonlarını alay etmenizi sağlar . Testlerinize yüklediğinizi varsayalım (repo benioku bölümüne bakınız) yukarıda yazdığım test aynı şekilde olur:

class CustomPostTypes extends Custom_TestCase {

  function test_init() {
     $r = new RegisterCustomPostType;
     // tests that the action was added with given arguments
     \WP_Mock::expectActionAdded( 'init', array( $r, 'register_post_type' ) );
     $r->init();
  }

  function test_register_post_type() {
     // tests that the function was called with given arguments and run once
     \WP_Mock::wpFunction( 'register_post_type', array(
        'times' => 1,
        'args' => array( 'foo' ),
     ) );
     $r = new RegisterCustomPostType;
     $r->register_post_type();
  }

}

Basit değil mi? Bu cevap bir öğretici değildir WP_Mock, bu yüzden daha fazla bilgi için repo benioku okuyun, ancak yukarıdaki örneğin oldukça açık olması gerektiğini düşünüyorum.

Dahası, alaycı add_actionveya register_post_typekendiniz yazmanız veya herhangi bir global değişkeni korumanız gerekmez .

Ve WP dersleri?

WP'nin de bazı sınıfları var ve eğer testleri çalıştırdığınızda WordPress yüklü değilse, onları alay etmeniz gerekir.

Bu, alaycı işlevlerden çok daha kolaydır, PHPUnit'in nesneleri alay etmek için gömülü bir sistemi vardır, ancak burada size Mockery'i önermek istiyorum. Çok güçlü bir kütüphane ve kullanımı çok kolay. Dahası, bu bir bağımlılıktır WP_Mock, yani eğer varsa, alaycınız da vardır.

Ama ne hakkında WP_UnitTestCase?

WordPress test paketi WordPress çekirdeğini test etmek için oluşturulmuştur ve çekirdeğe katkıda bulunmak istiyorsanız çok önemlidir, ancak onu eklentiler için kullanmak sizi yalnızca izolasyon halinde değil test etmenizi sağlar.

Gözlerinizi WP dünyasına koyun: çok sayıda modern PHP çerçevesi ve CMS var ve bunların hiçbiri eklenti / modülleri / uzantıları (veya ne denirse) test kodunu kullanarak önermiyor.

Fabrikaları, süitin kullanışlı bir özelliğini özlüyorsanız, orada harika şeyler olduğunu bilmeniz gerekir .

Gotchas ve dezavantajları

Burada önerdiğim iş akışının eksik olduğu bir durum var: özel veritabanı testi .

Eğer (en düşük seviyesinde orada yazmak için standart WordPress tabloları ve işlevlerini kullanın Aslında, $wpdbyöntemlere) yapmanız gerekir asla aslında veri ise yazma veri veya testinde aslında veritabanında sadece emin uygun yöntemler doğru argümanlarla dendiğini olmak.

Ancak, eklentileri orada yazmak için sorgular oluşturan özel tablolar ve işlevlerle yazabilir ve bu sorgular işe yarayıp yaramadığını test edebilir.

Bu durumlarda, WordPress test paketi size çok yardımcı olabilir ve bazı durumlarda gibi işlevleri çalıştırmak için WordPress'in yüklenmesi gerekebilir dbDelta.

(Testler için farklı bir db kullanmanıza gerek yok, değil mi?)

Neyse ki PHPUnit size testlerin her şey bırakarak WordPress ortamı (veya bunun bir kısmını) yüklemek özel veritabanı testleri için paketi yazabilir böylece, ayrı ayrı çalıştırılabilir "suit" daki testler düzenlemenize olanak sağlar WordPress içermeyen .

Yalnızca diğer tüm eklenti sınıflarının bunlardan faydalanabileceği şekilde olabildiğince fazla veritabanı işlemi özetleyen sınıflar yazdığınızdan emin olun, böylece alay kullanarak sınıfların çoğunu veritabanıyla uğraşmadan düzgün bir şekilde test edebilirsiniz.

Üçüncü kez, kolayca yalıtım test edilebilir kod yazmak daha iyi kod yazmak demektir.


5
Kutsal bok, bir sürü faydalı bilgi! Teşekkür ederim! Her nasılsa, birim testin bütün noktasını kaçırmayı başardım (şimdiye dek yalnızca Code Dojo'nun içinde PHP testi yapıyordum). Ayrıca bugün erken saatlerde wp_mock'i ​​de buldum, ancak bazı nedenlerden dolayı bunu görmezden gelmeyi de başardım. Beni sinirlendiren, herhangi bir testin, ne kadar küçük olursa olsun, çalışması için en az iki saniye almasıydı (önce WP env'i yükleyin, testi ikinci uygulayın). Gözlerimi açtığın için tekrar teşekkür ederim!
Ionut Staicu

4
Thanks @IonutStaicu WordPress'i
yüklememenin

6
Ayrıca WP Core birim test çerçevesinin, WP'nin kendisiyle iyi bir şekilde bütünleşmesini sağlamak için otomatik testler olacak olan INTEGRATION testlerini yürütmek için harika bir araç olduğuna dikkat çekmeye değer (örneğin, tesadüfi fonksiyon ismi çarpışmaları vb. Yoktur).
John P Bloch

1
@JohnPBloch +1 iyi nokta için. Bir ad boşluğu kullanmak bile, her şeyin global olduğu WordPress'teki işlev adlarının çarpışmasından kaçınmak için yeterli olsa bile :) Tabii ki, entegrasyonlar / fonksiyonel testler bir şeydir. Şu anda Behat + Mink ile oynuyorum ama hala bununla pratik yapıyorum.
gmazzap

1
WordPress UnitTest ormanı üzerindeki "helikopter yolculuğu" için teşekkürler - Bu epik resme hala gülüyorum ;-)
Birgire 12:14
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.