Cucumber-js: Como posso especificar o arquivo de definição de etapa para cada arquivo de recurso?

Criado em 2 fev. 2017  ·  18Comentários  ·  Fonte: cucumber/cucumber-js

Meu gol

Estou tentando criar uma estrutura escalável de recursos e definições de etapa para um aplicativo grande e minha primeira tentativa foi tentar vincular arquivos step_definition a recursos para que eu pudesse usar o mesmo padrão de etapa para diferentes definições de etapa.

Meu código

Mostro meu exemplo atual:

Minha estrutura de pastas:

/features/sample.feature
/features/example.feature
/features/step_definitions/sample_steps.js
/features/step_definitions/example_steps.js
/features/step_definitions/common/common_steps.js

No meu sample.feature eu tenho:

  Scenario: Launching Cucumber
  Given I have some step definitions
   When I check some step definition with parameter "any"
   Then I should see all green "sample"

No meu example.feature eu tenho:

  Scenario: Launching Cucumber
  Given I have some step definitions
   When I check some step definition with parameter "any"
   Then I should see all green "example"

As etapas Given e When são definidas no arquivo /common/common_steps.js e funcionam bem.

A etapa Then é definida para sample_steps.js e example_steps.js mas de forma diferente.

No meu sample_steps.js eu tenho:

Then('I should see all green {stringInDoubleQuotes}', (arg) => {
   if (arg !== 'sample') {
     throw 'I should see all green when the argument is "sample"';
   }
   return;
 });

E, finalmente, no meu example_steps.js eu tenho:

Then('I should see all green {stringInDoubleQuotes}', (arg) => {
   if (arg !== 'example') {
     throw 'I should see all green when the argument is "example"';
   }
   return;
 });

O erro

Meu principal objetivo é ter tudo verde aqui, mas é claro que não funciona e recebo este erro obviamente:

Multiple step definitions match:
   I should see all green {stringInDoubleQuotes} - features\step_definitions\example_steps.js:6
   I should see all green {stringInDoubleQuotes} - features\step_definitions\sample_steps.js:6

Pepino-JVM

Eu sei que em pepino-jvm podemos especificar um atributo glue que vincula recursos e step_definitions e é exatamente o que estou procurando, mas em pepino-js. Exemplo em Java:

@RunWith(Cucumber.class)
@Cucumber.Options( glue = { "com.app.stepdefinitions.common", "com.app.stepdefinitions.sample" } )
public class SampleFeature{
}

@RunWith(Cucumber.class)
@Cucumber.Options( glue = { "com.app.stepdefinitions.common", "com.app.stepdefinitions.example" } )
public class ExampleFeature{
}

Finalmente

Como posso conseguir o mesmo que cucumbr-jvm usando pepino-js?

Comentários muito úteis

O escopo da IMO é definitivamente necessário. À medida que seu aplicativo cresce, o número de recursos se expande e você acabará com descrições conflitantes em diferentes contextos.

Na minha empresa, nosso produto tem centenas de recursos e o controle de qualidade tem casos de teste na faixa de 100k.
Se usarmos cucumber , definitivamente teremos esse problema.

Sim, acho que você deve considerar o contexto em vez do escopo.
Você pode ter a maioria, se não todas, de suas definições de etapa no contexto padrão (ou sem contexto), mas deve haver uma maneira de especificar o contexto sem a necessidade de uma DSL personalizada.

É o BA e o QA que devem escrever esses testes e qualquer DSL personalizado é obrigado a criar confusão e resistência.

O Recurso/Cenário já fornece contextos por definição, por isso a sintaxe do Gherkin tem o recuo.

Adicionar tags e DSL personalizado é uma solução alternativa de limitação de implementação (ou seja, um hack, IMO) em vez de uma solução.

Talvez você possa considerar isso enquanto considera https://github.com/cucumber/cucumber-js/issues/745 ?

Que tal estender Scenario de defineStep e passar {Given, When, Then} para o callback?

ou seja:

import {defineSupportCode} from 'cucumber'

defineSupportCode(({ Scenario, Given, When, Then }) => {
  Given(...)
  When(...)
  Then(...)
  Scenario('<match scenario description>', ({ Given, When, Then}) => {
    Given('<take precedent of non-contexted>', ...)
    ...
  })
})

Todos 18 comentários

Estou tentando criar uma estrutura escalável de recursos e definições de etapa para um aplicativo grande e minha primeira tentativa foi tentar vincular arquivos step_definition a recursos para que eu pudesse usar o mesmo padrão de etapa para diferentes definições de etapa.

Muito interessante. O pepino-js não tem nada embutido como o exemplo do pepino-java que você deu. A ideia que eu gosto para esse tipo de coisa é empurrar essa lógica de comutação para sua configuração ou instância de mundo. De qualquer forma, você acaba com apenas uma definição de etapa. Você pode ter várias definições de mundo entre as quais alternar ao definir seu código de suporte ou ter uma única instância de mundo que expõe métodos diferentes com base no contexto. No momento, não expomos o arquivo do cenário atual às etapas em execução, mas você pode usar tags para determinar seu contexto.

Na verdade, temos a mesma necessidade para isso. Estamos usando nightwatch-cucumber para executar testes de selênio e nossa única solução por enquanto é adicionar um prefixo a cada etapa:

Given [comp1] I click on "Open dialog"

vs

Given [comp2] I click on "Open dialog"

Isso nos ajuda a evitar definições de etapas ambíguas, mas leva a arquivos de recursos realmente ilegíveis.
Eu tentei mexer com a fonte do pepino.js, mas não encontrei boas dicas para adicionar suporte para esse recurso.

Isso poderia ser realizado com alguns ganchos ou mundos alternativos?

@leipert qual interface de usuário você está imaginando? Eu acredito que essa lógica deve viver no mundo ou suporte para vários objetos do mundo. Em seguida, as definições de etapa lidam apenas com a análise das correspondências do gherkin e passando-as para a função de mundo adequada. Poderíamos adicionar uma opção CLI para selecionar qual objeto de mundo usar.

No momento, se você gostaria de ter vários mundos / definições de etapas, você pode conseguir isso colocando seu código em pastas separadas e exigindo apenas uma delas por execução (usando a opção --require CLI).

Bem, "o livro do pepino" desencoraja especificamente essa maneira de projetar etapas. A etapa é compartilhada entre os cenários por design, a mesma frase deve ter um significado consistente, apesar do recurso que a usa. Depois de introduzir o escopo das etapas, é muito fácil criar uma armadilha de idioma.

Até agora, apenas as tags estão próximas do seu propósito no cububer.js. Mas apenas os ganchos podem declarar que são específicos das tags. Se você tiver certeza de que é o caminho certo para o seu pessoal, você pode inventar uma DSL, talvez simples como [nome do recurso] no padrão de etapas.

Obrigado, @pinxue . Resposta muito útil. No entanto, não consigo entender:

Bem, "o livro do pepino" desencoraja especificamente essa maneira de projetar etapas.

Uma mesma frase de ação pode ter significados diferentes em contextos diferentes. Mas tudo bem. É bom saber as opções que tenho, na verdade já estamos andando em uma DSL interna para conseguir,

Muito obrigado.

Obrigado pelas respostas @pinxue e @robsonrosa.
Aqui está meu raciocínio para definições de etapas com escopo:

  1. _As variáveis ​​globais são ruins e não são dimensionadas_:
    Temos apenas cerca de 15 arquivos de arquivo de recursos com 10 cenários cada, e já é difícil estruturar todas as definições de etapas e arquivos de recursos. Para grandes aplicativos (digamos, 100 arquivos de recursos com 10 cenários com uma média de 5 etapas), ter todas as etapas definidas globalmente parece insano, pois é muito difícil rastrear qual arquivo de recurso usa quais etapas.
  2. _Os desenvolvedores não têm controle sobre o texto nos arquivos de recursos_:
    Em nosso caso de uso, nosso "gerenciamento" grava arquivos de recursos, é difícil vendê-los: fazemos BDD incrível, mas você precisa se limitar a um DSL personalizado estranho.
  3. _Se o pepino-jvm tem a opção, por que o pepino-js não tem?_
    Este pode ser um mau argumento 💃

Eu vejo as seguintes abordagens para resolver o problema para nós:

  1. Crie um DSL personalizado com prefixos nas definições de etapas
    Contras: Não é bom trabalhar com ele.
    Prós: não precisa de alterações no pepino.js
  2. Crie glue para pepino.js
    Contras: Pode ir contra a ideia de "The Cucumber Book".
    Prós: Paridade de recursos com cucumber.js
  3. Alterar como executamos os testes
    Contras: Este recurso estará disponível para menos pessoas.
    Prós: não precisa de alterações no pepino.js

Dito isto, provavelmente iremos com o 3., se os mantenedores do pepino.js acharem que as definições de etapas com escopo estão fora do escopo (trocadilho intencional) para este projeto.

@robsonrosa Eu estaria interessado em sua solução, assim que você tiver uma.

O escopo da IMO é definitivamente necessário. À medida que seu aplicativo cresce, o número de recursos se expande e você acabará com descrições conflitantes em diferentes contextos.

Na minha empresa, nosso produto tem centenas de recursos e o controle de qualidade tem casos de teste na faixa de 100k.
Se usarmos cucumber , definitivamente teremos esse problema.

Sim, acho que você deve considerar o contexto em vez do escopo.
Você pode ter a maioria, se não todas, de suas definições de etapa no contexto padrão (ou sem contexto), mas deve haver uma maneira de especificar o contexto sem a necessidade de uma DSL personalizada.

É o BA e o QA que devem escrever esses testes e qualquer DSL personalizado é obrigado a criar confusão e resistência.

O Recurso/Cenário já fornece contextos por definição, por isso a sintaxe do Gherkin tem o recuo.

Adicionar tags e DSL personalizado é uma solução alternativa de limitação de implementação (ou seja, um hack, IMO) em vez de uma solução.

Talvez você possa considerar isso enquanto considera https://github.com/cucumber/cucumber-js/issues/745 ?

Que tal estender Scenario de defineStep e passar {Given, When, Then} para o callback?

ou seja:

import {defineSupportCode} from 'cucumber'

defineSupportCode(({ Scenario, Given, When, Then }) => {
  Given(...)
  When(...)
  Then(...)
  Scenario('<match scenario description>', ({ Given, When, Then}) => {
    Given('<take precedent of non-contexted>', ...)
    ...
  })
})

Eu aprendo o BDD em http://fitnesse.org/ , então posso olhar para o BDD de forma diferente da comunidade do pepino.

Paginação @richardlawrence

Esta é uma área onde o pepino é particularmente opinativo. Ele é construído em torno da crença de que as equipes devem desenvolver uma linguagem onipresente, onde palavras e frases significam exatamente uma coisa no contexto de um aplicativo e são usadas de forma consistente. Manter as definições de passo globais mantém uma pressão positiva para evitar ambiguidade.

Nos raros casos em que um aplicativo tem vários contextos delimitados distintos (em termos DDD), você simplesmente dividiria seu conjunto Cucumber nas mesmas linhas em que seu aplicativo é dividido para refletir esse limite e os defs de etapa seriam globais dentro de cada contexto delimitado.

Um artigo sobre como trabalhar com o Cucumber e criar limites. Ele implementa algumas das ideias nesta página, mas não apresenta grandes soluções, pois @richardlawrence mencionou que você não pode configurar o Cucumber para se concentrar em uma classe específica para definições de etapas. http://confessionsofanagilecoach.blogspot.com/2017/05/teaching-cucumbers-about-boundaries.html

Como @leipert disse, variáveis ​​globais são ruins. Eu acho que aqueles de nós no mundo CucumberJVM estão entendendo apenas metade da história. O Cucumber (não JVM) usa um conceito de mundo organizado, que é um espaço de memória global para a duração do Cenário . Após a execução do Cenário, ele é destruído. Isso parece uma boa solução. Mas... não estou vendo uma boa implementação deste padrão no CucumberJVM. Portanto, se o Cucumber nos obriga a ter um namespace plano/global para definições de etapas, e o CucumberJVM tem um padrão de design claro para implementar o padrão World, estou um pouco mais feliz. Até este ponto, CucumberJVM + padrão World == soluções excessivamente complicadas. Até agora, tudo o que vi é mais complicado do que simplesmente me deixar controlar quais funções de etapa combinam com qual arquivo de recurso. Até agora as alternativas que vi não me dão nada mais valioso por todo o esforço/complexidade. Outros tipos de pepino têm soluções mundiais melhores.

Em última análise, mesmo com o padrão World, ainda estou insatisfeito porque sei que haverá perda de informações ao passar do arquivo de recurso para as etapas. Se eu fizer um grande esforço: colocar meus arquivos de recursos em uma boa organização, criar bons nomes de arquivos de recursos, declarar meu recurso de maneira inteligente, nomear meus cenários de maneira inteligente - todo o contexto é jogado pela janela e sou forçado para trabalhar com o namespace de definição de etapa global.

Só consigo pensar em manter o relacionamento fora das validações do sistema e simplesmente adicionar curingas nos caminhos para os testes, e fazê-los funcionar, mesmo que seja preciso modificar algum framework open source. pode dar uma chance à medida que nosso projeto cresce.

Entendo seu objetivo, mas não recomendo.
Eu também recomendaria ter arquivos .feature explícitos, com uma declaração de plano de fundo ou uma etapa dada adicional que torne isso explícito.

Alternativamente, você pode criar dois arquivos de configuração diferentes.
Um para a amostra e outro para o exemplo.
Então você pode apontar para diferentes pastas de etapas.

@robsonrosa @leipert compartilho sua opinião
Quase dois anos depois do post original...
Conseguiu algum progresso com isso? Alguma solução alternativa?

@cristianmercado19 Desculpe, não. Não estou mais usando pepino js.

Ups... Eu gosto da maneira de escrever o arquivo de recurso completamente isolado da implementação da etapa.
Tentei atingir o mesmo objetivo com _mocha_, mas não estou satisfeito.
Se você tiver alguma outra alternativa que comprometa meu objetivo, mais do que feliz em tentar.
Agradeço sua ajuda @leipert .

Este tópico foi bloqueado automaticamente, pois não houve nenhuma atividade recente depois que ele foi fechado. Por favor, abra um novo problema para bugs relacionados.

Esta página foi útil?
0 / 5 - 0 avaliações