Teste isolado
Ao desenvolver um plugin, a melhor maneira de testá-lo é sem carregar o ambiente WordPress.
Se você escrever um código que possa ser facilmente testado sem o WordPress, seu código se tornará melhor .
Todo componente que é testado em unidade deve ser testado isoladamente : ao testar uma classe, você só precisa testar essa classe específica, assumindo que todos os outros códigos estejam funcionando perfeitamente.
Esta é a razão pela qual os testes de unidade são chamados de "unidade".
Como um benefício adicional, sem carregar o núcleo, seu teste será executado muito mais rápido.
Evite ganchos no construtor
Uma dica que posso lhe dar é evitar colocar ganchos nos construtores. Essa é uma das coisas que tornará seu código testável isoladamente.
Vamos ver o código de teste no OP:
class CustomPostTypes extends WP_UnitTestCase {
function test_custom_post_type_creation() {
$this->assertTrue( post_type_exists( 'foo' ) );
}
}
E vamos supor que esse teste falhe . Quem é o culpado ?
- o gancho não foi adicionado ou não está corretamente?
- o método que registra o tipo de postagem não foi chamado de todo ou com argumentos errados?
- existe um bug no WordPress?
Como pode ser melhorado?
Vamos supor que seu código de classe seja:
class RegisterCustomPostType {
function init() {
add_action( 'init', array( $this, 'register_post_type' ) );
}
public function register_post_type() {
register_post_type( 'foo' );
}
}
(Nota: vou me referir a esta versão da turma pelo restante da resposta)
A maneira como escrevi essa classe permite criar instâncias da classe sem chamar add_action
.
Na classe acima, há duas coisas a serem testadas:
- o método
init
realmente chama add_action
passando a ele argumentos adequados
- o método
register_post_type
realmente chama register_post_type
função
Eu não disse que você precisa verificar se o tipo de postagem existe: se você adicionar a ação apropriada e se ligar register_post_type
, o tipo de postagem personalizado deverá existir: se não existir, será um problema no WordPress.
Lembre-se: ao testar seu plug-in, você deve testar seu código, não o código do WordPress. Nos seus testes, você deve assumir que o WordPress (como qualquer outra biblioteca externa que você usa) funciona bem. Esse é o significado do teste de unidade .
Mas ... na prática?
Se o WordPress não estiver carregado, se você tentar chamar os métodos de classe acima, você receberá um erro fatal e precisará zombar das funções.
O método "manual"
Claro que você pode escrever sua biblioteca de zombaria ou zombar "manualmente" de todos os métodos. É possível. Vou lhe dizer como fazer isso, mas depois mostrarei um método mais fácil.
Se o WordPress não for carregado enquanto os testes estiverem sendo executados, significa que você pode redefinir suas funções, por exemplo, add_action
ou register_post_type
.
Vamos supor que você tenha um arquivo carregado a partir do seu arquivo de inicialização, onde você tem:
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();
}
Reescrevi as funções para simplesmente adicionar um elemento a uma matriz global toda vez que elas são chamadas.
Agora você deve criar (se ainda não tiver um) sua própria classe de caso de teste de base estendendo PHPUnit_Framework_TestCase
: isso permite que você configure facilmente seus testes.
Pode ser algo como:
class Custom_TestCase extends \PHPUnit_Framework_TestCase {
public function setUp() {
$GLOBALS['counter'] = array();
}
}
Dessa forma, antes de cada teste, o contador global é redefinido.
E agora seu código de teste (refiro-me à classe reescrita que publiquei acima):
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' ) );
}
}
Você deve observar:
- Consegui chamar os dois métodos separadamente e o WordPress não é carregado. Dessa forma, se um teste falhar, eu sei exatamente quem é o culpado.
- Como eu disse, aqui testo que as classes chamam funções WP com argumentos esperados. Não há necessidade de testar se o CPT realmente existe. Se você está testando a existência do CPT, está testando o comportamento do WordPress, não o comportamento do seu plug-in ...
Bom .. mas é uma PITA!
Sim, se você precisar zombar manualmente de todas as funções do WordPress, é realmente uma dor. Um conselho geral que posso dar é usar o menor número possível de funções WP: você não precisa reescrever o WordPress, mas funções abstratas do WP que você usa em classes personalizadas, para que possam ser ridicularizadas e facilmente testadas.
Por exemplo, no exemplo acima, você pode escrever uma classe que registra os tipos de postagem, chamando register_post_type
'init' com os argumentos fornecidos. Com essa abstração, você ainda precisa testar essa classe, mas em outros locais do seu código que registram tipos de postagem, você pode fazer uso dessa classe, zombando dela em testes (supondo que ela funcione).
O mais impressionante é que, se você escrever uma classe que abstraia o registro do CPT, poderá criar um repositório separado para ela e, graças a ferramentas modernas como o Composer, ele será incorporado em todos os projetos onde você precisar: teste uma vez, use em qualquer lugar . E se você encontrar um bug nele, poderá corrigi-lo em um só lugar e com um simples composer update
todos os projetos em que ele é usado também serão corrigidos.
Pela segunda vez: escrever código testável isoladamente significa escrever código melhor.
Mas mais cedo ou mais tarde eu preciso usar as funções do WP em algum lugar ...
Claro. Você nunca deve agir paralelamente ao núcleo, não faz sentido. Você pode escrever classes que agrupam as funções do WP, mas essas classes também precisam ser testadas. O método "manual" descrito acima pode ser usado para tarefas muito simples, mas quando uma classe contém muitas funções do WP, pode ser uma dor.
Felizmente, lá existem pessoas boas que escrevem coisas boas. A 10up , uma das maiores agências de WP, mantém uma biblioteca muito boa para pessoas que desejam testar plugins da maneira certa. É WP_Mock
.
Ele permite que você zombe das funções do WP e ganchos . Supondo que você tenha carregado em seus testes (consulte o repositório leia-me), o mesmo teste que escrevi acima se torna:
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();
}
}
Simples, não é? Esta resposta não é um tutorial para WP_Mock
, então leia o readme do repositório para obter mais informações, mas o exemplo acima deve ser bem claro, eu acho.
Além disso, você não precisa escrever nenhuma zombaria add_action
ou register_post_type
sozinho, nem manter nenhuma variável global.
E aulas WP?
O WP também tem algumas classes e, se o WordPress não estiver carregado ao executar testes, você precisará zombar delas.
Isso é muito mais fácil do que zombar de funções, o PHPUnit possui um sistema incorporado para zombar de objetos, mas aqui quero sugerir o Mockery para você. É uma biblioteca muito poderosa e muito fácil de usar. Além disso, é uma dependência de WP_Mock
, portanto, se você o tiver, também terá Zombaria.
Mas que tal WP_UnitTestCase
?
O conjunto de testes do WordPress foi criado para testar o núcleo do WordPress e, se você quiser contribuir com o núcleo, é essencial, mas usá-lo para plug-ins faz com que você não teste isoladamente.
Coloque seus olhos no mundo do WP: existem muitas estruturas PHP modernas e CMS por aí e nenhuma delas sugere testar plugins / módulos / extensões (ou como eles são chamados) usando o código da estrutura.
Se você sente falta de fábricas, um recurso útil da suíte, precisa saber que há coisas incríveis por lá.
Pegadinhas e desvantagens
Há um caso em que o fluxo de trabalho que sugeri aqui não possui: teste de banco de dados personalizado .
De fato, se você usar tabelas e funções padrão do WordPress para escrever lá (nos $wpdb
métodos de nível mais baixo ), nunca precisará realmente escrever dados ou testar se os dados estão realmente no banco de dados, apenas certifique-se de que os métodos adequados sejam chamados com argumentos adequados.
No entanto, você pode escrever plug-ins com tabelas e funções personalizadas que criam consultas para serem escritas lá e testar se essas consultas funcionam, é de sua responsabilidade.
Nesses casos, a suíte de testes do WordPress pode ajudá-lo muito, e o carregamento do WordPress pode ser necessário em alguns casos para executar funções como dbDelta
.
(Não é necessário dizer para usar um db diferente para testes, não é?)
Felizmente, o PHPUnit permite que você organize seus testes em "suítes" que podem ser executadas separadamente, para que você possa criar um conjunto para testes de banco de dados personalizados, onde você carrega o ambiente WordPress (ou parte dele), deixando todo o restante dos testes sem WordPress .
Apenas certifique-se de escrever classes que abstraiam o maior número possível de operações de banco de dados, de maneira que todas as outras classes de plug-ins as utilizem, para que, usando o mock, você possa testar adequadamente a maioria das classes sem lidar com o banco de dados.
Pela terceira vez, escrever código facilmente testável isoladamente significa escrever código melhor.
phpunit
, consegue ver os testes reprovados ou com falha? Você instaloubin/install-wp-tests.sh
?