Fork us

Hooking no Processo de Teste

Você aprendeu como escrever definições de etapas e que com Gherkin você pode mover etapas comuns em um bloco de fundo, e fazer suas funcionalidades DRY - Don’t repeat yourself (livre de ambiguidades). Mas e se isso não é suficiente? E se você precisar executar algum código antes de toda a suite de teste ou depois de um cenário específico? Hooks é a salvação:

// features/bootstrap/FeatureContext.php

use Behat\Behat\Context\Context;
use Behat\Testwork\Hook\Scope\BeforeSuiteScope;
use Behat\Behat\Hook\Scope\AfterScenarioScope;

class FeatureContext implements Context
{
    /**
     * @BeforeSuite
     */
     public static function preparar(BeforeSuiteScope $scope)
     {
         // preparar o sistema para a suite de teste
         // antes de executar isto
     }

     /**
      * @AfterScenario @database
      */
     public function limparDB(AfterScenarioScope $scope)
     {
         // limpa a database depois dos cenários,
         // marcados com @database
     }
}

Sistema de Hook Behat

O Behat fornece um número de pontos de hook que nos permitem executar a lógica arbitrária em vários pontos no ciclo de teste Behat. Hooks são muito parecidos com definições de etapas ou transformações - eles são métodos simples com anotações especiais dentro de suas classes de contexto. Não há associação entre o local onde o hook é definido e qual nó é executado, mas você pode usar tags ou hooks nomeados se você quiser um controle mais refinado.

Todos os hooks definidos são executados sempre que ações relevantes ocorram. A árvore de ações deve ser algo como isto:

├── Suite #1
│   ├── Funcionalidade #1
│   │   ├── Cenário #1
│   │   │   ├── Etapa #1
│   │   │   └── Etapa #2
│   │   └── Cenário #2
│   │       ├── Etapa #1
│   │       └── Etapa #2
│   └── Funcionalidade #2
│       └── Cenário #1
│           └── Etapa #1
└── Suite #2
    └── Funcionalidade #1
        └── Cenário #1
            └── Etapa #1

Este é o ciclo básico do teste no Behat. Há muitas suites de testes, cada uma das quais tem muitas funcionalidades, que tem muitos cenários com muitas etapas. Note que quando o Behat realmente é executado, exemplos de esquemas de cenário são interpretados como cenários - ou seja, cada exemplo torna-se um cenário real nesta árvore ação.

Hooks

Os Hooks permitem você executar seu código customizado pouco antes ou pouco depois de cada uma destas ações. O Behat permite vocẽ utilizar os seguintes hooks:

  1. O hook BeforeSuite é executado antes que qualquer funcionalidade em sua suite rode. Por exemplo, você pode usar isto para configurar o projeto que você está testando. Este hook recebe um argumento opcional com uma instância da classe Behat\Testwork\Hook\Scope\BeforeSuiteScope.
  2. O hook AfterSuite é executado depois que todas as funcionalidades da sua suite tenham sido executadas. Este hook é útil para despejar ou imprimir algum tipo de estatística ou derrubar a sua aplicação após o teste. Este hook recebe um argumento opcional com uma instância da classe Behat\Testwork\Hook\Scope\AfterSuiteScope.
  3. O hook BeforeFeature é executado antes que a funcionalidade seja executada. Este hook recebe um argumento opcional com uma instância da classe Behat\Behat\Hook\Scope\BeforeFeatureScope.
  4. O hook AfterFeature é executado após o Behat finalizar a execução da funcionalidade. Este hook recebe um argumento opcional com uma instância da classe Behat\Behat\Hook\Scope\AfterFeatureScope.
  5. O hook BeforeScenario é executado antes que um cenário específico seja executado. Este hook recebe um argumento opcional com uma instância da classe Behat\Behat\Hook\Scope\BeforeScenarioScope.
  6. O hook AfterScenario é executado depois que o Behat termina a execução de um cenário. Este hook recebe um argumento com uma instância da classe Behat\Behat\Hook\Scope\AfterScenarioScope.
  7. O hook BeforeStep é executado antes que uma etapa é executada. Este hook recebe um argumento opcional com uma instância da classe Behat\Behat\Hook\Scope\BeforeStepScope.
  8. O hook AfterStep é executado depois que o Behat termina de executar uma etapa. Este hook recebe um argumento opcional com uma instância da classe Behat\Behat\Hook\Scope\AfterStepScope.

Você pode utilizar qualquer um destes hooks colocando como anotação em qualquer um dos seus métodos na classe contexto:

/**
 * @BeforeSuite
 */
public static function preparar($scope)
{
    // preparar o sistema para suite de testes
    // antes de executar isto
}

Nós utilizamos anotações como fizemos antes com definitions. Simplesmente utilizando a anotação do nome da hook que você deseja usar (por exemplo @BeforeSuite).

Hooks de Suite

Suite hooks são executadas fora do contexto do cenário. Isso significa que sua classe de contexto (por exemplo FeatureContext) ainda não foi instanciada e a única maneira que o Behat pode executar o código é através de chamadas estáticas. Este é o motivo das suite hooks precisarem ser definidas com métodos estáticos na classe de contexto:

use Behat\Testwork\Hook\Scope\BeforeSuiteScope;
use Behat\Testwork\Hook\Scope\AfterSuiteScope;

/** @BeforeSuite */
public static function configurar(BeforeSuiteScope $scope)
{
}

/** @AfterSuite */
public static function destruir(AfterSuiteScope $scope)
{
}

Aqui estão dois tipos de suite hook disponíveis:

  • @BeforeSuite - executado antes de qualquer funcionalidade.
  • @AfterSuite - executado após a execução de todas as funcionalidades.

Hooks de Funcionalidade

Como as hooks de suite, hooks de funcionalide também são executadas fora do contexto de cenário. Então como uma hook de suite, sua hook de funcionalidade precisa ser definida como método estático em seu contexto:

use Behat\Behat\Hook\Scope\BeforeFeatureScope;
use Behat\Behat\Hook\Scope\AfterFeatureScope;

/** @BeforeFeature */
public static function configurarFuncionalidade(BeforeFeatureScope $scope)
{
}

/** @AfterFeature */
public static function destruirFuncionalidade(AfterFeatureScope $scope)
{
}

Aqui estão dois tipos de hook de funcionalidade disponíveis:

  • @BeforeFeature - é executado antes de cada funcionalidade na suite.
  • @AfterFeature - é executado depois de cada funcionalidade na suite.

Hooks de Cenário

Hooks de cenário são disparadas antes ou depois que cada cenário é executado. Estes hooks são executados dentro da inicialização da instância do contexto, assim eles não só poderiam ser instância de métodos de contexto, eles também terão acesso a qualquer propriedade do objeto que você definiu durante o seu cenário:

use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Behat\Hook\Scope\AfterScenarioScope;

/** @BeforeScenario */
public function antes(BeforeScenarioScope $scope)
{
}

/** @AfterScenario */
public function depois(AfterScenarioScope $scope)
{
}

Aqui estão dois tipos de hook de cenário disponíveis:

  • @BeforeScenario - executado antes da execução de todos os cenário em cada funcionalidade.
  • @AfterScenario - executado após a execução de todos os cenário em cada funcionalidade.

Agora, a parte interessante:

O hook @BeforeScenario executa não só antes de cada cenário em cada funcionalidade, mas antes de cada linha de exemplo no esquemas do cenário. Sim, cada linha de exemplo do esquema do cenário trabalha quase do mesmo modo que um cenário comum.

@AfterScenario funcionam exatamente do mesmo modo, sendo executado tanto após cenários habituais e exemplos de saída.

Hooks de Etapas

Hooks de etapas são disparadas antes ou depois que cada etapa é executada. Estes hooks são executados dentro da inicialização da instância do contexto, por isso eles são métodos de instância do mesmo modo que os hooks de cenário são:

use Behat\Behat\Hook\Scope\BeforeStepScope;
use Behat\Behat\Hook\Scope\AfterStepScope;

/** @BeforeStep */
public function antesDaEtapa(BeforeStepScope $scope)
{
}

/** @AfterStep */
public function depoisDaEtapa(AfterStepScope $scope)
{
}

Aqui estão dois tipos disponíveis de hook de etapa:

  • @BeforeStep - executado antes de cada etapa em cada cenário.
  • @AfterStep - executado depois de cada etapa em cada cenário.

Hooks Tagueadas

Talvez as vezes você queira executar somente um certo hook para certos cenários, funcionalidades ou etapas. Isto pode ser obtido através da associação da hook @BeforeFeature, @AfterFeature, @BeforeScenario, @AfterScenario, @BeforeStep ou @AfterStep com uma ou mais tags. Você pode também usar tags OR (||) e AND (&&):

/**
 * @BeforeScenario @database,@orm
 */
public function limparBancoDeDados()
{
    // limpar banco de dados antes
    // do cenario @database ou @orm
}

Utilize a tag && para executar somente uma hook quando tem a tag all:

/**
 * @BeforeScenario @database&&@acessorios
 */
public function limparBancoDeDadosAcessorios()
{
    // limpar banco de dados acessorios
    // antes dos cenários @database @acessorios
}