Este artigo foi traduzido por máquina.

Interoperabilidade do .NET

Automatizar o teste de aceitação com IronRuby

Ben Hall

Download do código disponível na Galeria de código do MSDN
Procure o código on-line

Este artigo discute:
  • O que é o teste de aceitação?
  • Teste de aceitação automatizada
  • Testes de aceitação de implementação
Este artigo usa as seguintes tecnologias:
IronRuby

Conteúdo

O que É o teste de aceitação?
Teste de aceitação automatizada
A história
Cenários
Implementação de testes de aceitação
Cenário de RSpec Runner interação com objetos de C#
A interface do usuário de teste de aceitação
Mover para frente

Quase todas sonhos de desenvolvedor do dia quando requisitos do cliente podem ser comunicados corretamente sem precisar perder a funcionalidade de incompletas ou incorretas implementação tempo. Não seria ótimo se poderíamos mão uma especificação clara e legível para um cliente e peça que eles se isso corresponde a suas necessidades de recurso, após o qual poderia executamos exatamente a mesma especificação, sem qualquer esforço adicional, para verificar nossa implementação contra esses requisitos?

Esse nível de comunicação entre cliente e o desenvolvedor pode ser feito com êxito usando o conceito de especificações de teste e executáveis de aceitação. Tirando proveito do BDD (comportamento controlada por Development), podemos começar comunicar requisitos com mais eficiência.

Em meu artigo na edição de fevereiro de 2009 da MSDN Magazine (" Introdução ao IronRuby E RSpec, parte 1"), Eu introduzidos você IronRuby e demonstrou como ele permite usar a linguagem de rubi dinâmica para interoperar com código de compatível com. NET, como C#. Neste artigo, eu apresentará o conceito de teste de aceitação. Criando sobre os conceitos introduzidos no meu artigo anterior, VOU mostrar como o teste de aceitação pode ser automatizadas usando IronRuby e RSpec para verificar os aplicativos .NET e criar uma especificação executável para o sistema.

O que É o teste de aceitação?

Teste de aceitação foi associado a várias definições diferentes. Para mim, o teste de aceitação é mais informações sobre verificação de que o sistema em desenvolvimento atende aos requisitos do cliente e menos sobre a redução do número de erros em nosso código. Em outras palavras, teste de aceitação é não sobre o teste de código, mas sobre a criação de que o cliente ou empresa quer. Parece óbvio, mas a falta de teste de aceitação e semelhante não têm de compreensão dos requisitos são os motivos principais para várias falhas de projeto.

Teste de aceitação só funciona com o suporte do cliente ou em que o mínimo, um proxy para o cliente, para ajudar a definir os critérios. Sem alguém orientando os critérios de aceitação, você não têm nada a Verifique se o software está correto, que torna difícil Verifique se que você estiver criando o software correto. O cliente, junto com todos os os membros da equipe de desenvolvimento deve se unem para definir o sistema em termos de um número de teste "cenários" que descrevem o que o sistema deve fazer e como ele deve fazer isso.

Por exemplo, se nós foram desenvolvendo um iniciado comércio sistema, um teste de aceitação pode se basear a interação de carrinho de compras. Um cenário típico pode ser, "Se o carrinho de compras está vazio e adicionar produto 123, em seguida, a contagem de item deve aumentar para 1 e o subtotal deve ser de r$ 20,00." Isso fornece uma compreensão de como o cliente espera o carrinho de compras se comportar e também fornece algumas etapas para verificar a implementação. Conforme o sistema cresce, você teria mais cenários Verificando partes diferentes do conjunto de sistema e de recurso.

Além disso, uma consideração importante ao escrever os cenários é a linguagem usada. O idioma deve refletir como o negócio entende o problema, não como um desenvolvedor deve implementar a solução. Se os testes foram descritos em termos de implementação real — "quando clico no botão 'Enviar', as alterações de 'Confirmar' rótulo" — isso irá fornecer o menor valor para o cliente e dependerá de como você realmente implementar o sistema. Se as alterações de implementação real, mas os requisitos de negócios permanecem o mesmo, em seguida, essa dependência exigirá os custos de manutenção adicionais como a equipe deve atualizar testes relacionados.

Criando testes com requisitos claros e critérios de passagem, seu software significa uma chance muito melhor de reunião as expectativas do cliente. No entanto, ainda assim isso envolve alguém verificar manualmente que os requisitos são atendidos e o aplicativo funciona conforme o esperado. Isso é onde entra a idéia de testes de aceitação automatizada. Em vez dos requisitos estar em um documento desatualizado em um compartilhamento de arquivo, os requisitos são definidos como exemplos e cenários são verificados no controle de origem com os artefatos de implementação e podem ser executados em qualquer ponto para verificar se a qualquer requisito é implementado e funcionando corretamente. Você pode executar a mesma abordagem para escrever os testes, mas em vez de escrevê-los em caso de teste de software de gerenciamento ou em uma planilha, você escrevê-los diretamente no código

Teste de aceitação automatizada

Teste de aceitação irá ajudar a validar que você estiver criando o aplicativo que o cliente desejar, enquanto automatizar esses cenários permite que você constantemente verificar a implementação durante o processo de desenvolvimento e usá-los como parte do seu conjunto de testes de regressão para garantir que futuras alterações não violam requisitos presentes.

No entanto, ter um cliente envolvido escrita de testes, especialmente a testes automatizados, apresenta uma série de possíveis problemas. Os clientes, em geral, são e tendem a shy longe o desenvolvimento real de software. Através da colaboração com a equipe técnica, o cliente pode fornecer a entrada e exemplos enquanto os testadores ou desenvolvedores rapidamente podem codificar os cenários e especificação executável. Além disso, os cenários devem ser claras para qualquer pessoa na empresa. Usando IronRuby, você pode melhorar a legibilidade do testes, mas não como esse trabalho na sessão prática?

Para ilustrar como esse processo pode ser usado em um projeto real, VOU Mostrar a implementação de um usuário texto ao redor de um sistema de cálculo de preços. É um exemplo simples, mas esperamos que ele demonstrará as etapas envolvidas. Teste de aceitação é uma estrutura de como você pode abordar o problema e existem muitas variantes e interpretações. No entanto, isso deve fornecer os conceitos básicos permitindo que você ir adiante e descobrir que detalhadas trabalho de técnicas para você.

Este exemplo veio fora de uma apresentação "vermelho, verde, Refactor" que deu em um grupo de usuários local. Posteriormente, um dos participantes solicitado para Meu conselho em como ele foi efetivamente unidade testar seu aplicativo para garantir que todos os cálculos de preços, incluindo várias opções e adições ao pacote, foram sejam calculados corretamente. Este exemplo é um perfeito demonstração de onde a teste de aceitação é extremamente útil. Por ter um texto e o cenário em torno de preços e o que você pretende acontecer, você pode ter certeza de que o sistema está funcionando corretamente.

Essas verificações também passará a ser uma fonte útil de documentação para a equipe de desenvolvimento e os clientes para demonstrar diferentes exemplos de como o sistema funciona e as várias exceções. Se um cliente acha que um preço está incorreto, a equipe pode consultar a especificação de executável, que o cliente aprovado, como prova do trabalho concluído.

Sem ter que esses testes no lugar, as regras ao redor de preços provavelmente poderiam ser definidas em documentos do Word enormes tentando explicar como o sistema lida com diversas configurações de preços – documentos que são inerentemente desconectados do sistema sendo desenvolvido. Esse é o motivo pelo qual eu acredito em testes de aceitação automatizada.

A história

Ao desenvolver um sistema de cálculo de preço com o objetivo usando testes de aceitação, o primeiro estágio é ter todas as pessoas uma sala de definir o texto. Os clientes definir o que desejam com estímulo de técnicos membros da equipe. Com BDD, como foi mencionado o artigo anterior, deseja usar um formato de conjunto para definir os textos para oferecer suporte a um idioma consistente entre os membros de equipe e especificamente o que é necessário do cliente. Dado o formato a seguir, você poderia preencha os detalhes com a ajuda do cliente:

Como [função] quero [recurso] então que [resultado].

Um erro imediato é geralmente iniciar incluindo detalhes da implementação no texto. Por exemplo:

Como administrador, eu quero preços a serem exibidos em uma exibição de grade em default.aspx, para que possam fornecê-los com o custo.

O texto deve descrever o problema apenas em termos de negócios específicas e comandar desmarque de detalhes técnicos. Um exemplo melhor poderia ser isso:

Como um administrador de venda, eu quero preços a ser identificado para um cliente para que possam fornecê-los com o custo.

Infelizmente, enquanto ele segue o formato, ele fornece algumas informações muito flexíveis e ele não fornecer um ponto de partida para a implementação. Um exemplo ainda melhor seria:

Como um administrador de venda, eu quero poder exibir os preços para o produto x, para que eu possam fornecer os clientes com um custo exato com base nas suas necessidades.

Este texto nos fornece mais detalhes e mais contexto sobre o que o cliente está esperando acontecer e o que realmente desejam. Como resultado, é muito mais útil para nós ao implementar o sistema.

Um fato importante lembrar-se ao escrever artigos e recursos é para mantê-los como concentra-se possível. Ele facilitará muito criar os cenários, escrever os testes e implementar o código concluído. Em alguns sistemas, o texto pode se basear em torno de um recurso.

Cenários

Mesmo com um texto, você ainda precisa obter mais informações sobre o que você está esperando acontecer. Cenários são extremamente úteis para isso. Um cenário simplesmente informa que recebe um determinado contexto, se algo acontecer, então a saída deve ser isso. O objetivo é fornecer um exemplo mais concreto de como de implementação o texto deve se comportar em situações diferentes. Isso se comunica as expectativas para a equipe e fornece etapas para verificação.

A sintaxe recomendada para usar é conhecida como o fornecido, quando, em seguida, (GWT) sintaxe, promovido por Dan Norte:

Fornecido [contexto], quando [algo acontecer], em seguida, [resultado].

Um cenário de exemplo poderia ser algo assim:

Cenário: uma licença de usuário para o produto X sem suporte

Dado o produto X, ao usuário solicita a licença de usuário único E isso não inclui suporte E o preço deve ser de r $ 250,00.

O uso de "e" pode ser usado para conectar vários contextos ou resultados para permitir que você forneça mais significado para o cenário e fornecer mais informações sobre o que está acontecendo.

O ideal é que deve haver muitos cenários possíveis para resolver qualquer ambigüidade, servir como a especificação do executável e fornecer informações suficientes para o trabalho inicial para iniciar. É importante observar que essas situações não são corrigidas. À medida que o trabalho prosseguirá e mais conhecimento é obtido, talvez seja necessário voltar para o cliente com perguntas sobre cenários existentes ou criar cenários adicionais com base no conhecimento adquirido.

Ao escrever os cenários, há algumas coisas em mente. Semelhante a textos, cenários devem ser enfoca um exemplo. O principal motivo para isso é legibilidade. Se a situação é fazer muito, em seguida, a mensagem principal será perdida.

Implementação de testes de aceitação

Quando você tiver o texto e os cenários em um formato não criptografado, compreensível, o próximo estágio é automatizar o texto e os cenários. Isso permite que eles sejam executados durante o desenvolvimento para controlar o andamento e capturar erros de regressão. Enquanto que irá ser falando em termos de RSpec, o processo básico pode ser transferido para qualquer estrutura de BDD. Para obter detalhes sobre a instalação IronRuby e RSpec, consulte meu artigo mencionado anteriormente.

Se você seguir o exemplo anterior de escrever os textos, em seguida, você encontrará o runner cenário na RSpec muito natural. Em um arquivo vazio sem formatação, nesse caso, um arquivo chamado pricing_scenarios_for_product_x, você simplesmente copiar o texto e os cenários para o arquivo no seguinte formato:

Story: Pricing for New Product X
  As a sales administrator, I want to be able to view
  prices for product x so that I can provide customers
  with an accurate cost for their requirements. 
Scenario: Single User License for Product X without support
  Given Product X
  When user requests a 1 user license
  And this does not include support
  Then the price should be $250

Agora isso forma a base de nossos critérios de aceitação e será usado quando verificar o aplicativo. No entanto, para habilitá-los atuar como a especificação executável, você precisa alguns rubi código para executá-los. IronRuby é um ambiente fácil para iniciar gravação e execução do código. Basta criar um arquivo com uma extensão .RB e começar a escrever testes RSpec.

A primeira etapa é fazer referência a estrutura RSpec. Isso é feito por meio de exigem diretivas. Incluindo as duas linhas a seguir, você pode iniciar usando o RSpec texto runner:

require 'rubygems'
require 'spec/story'

Agora você pode definir as etapas. Uma etapa é uma linha no cenário usando o formato GWT. Com RSpec, uma etapa é associada um método no código. Cada etapa deve ser associada a fazer apenas uma tarefa. Por exemplo, a etapa de dado é usado para configurar os objetos, enquanto E geralmente é onde ocorre a declaração e a verificação.

Com RSpec, todas as etapas precisam ser disposto em um bloco chamado steps_for. Dentro dos colchetes é um identificador para esse conjunto de etapas:

steps_for(:product_x) do
  #Steps go here
end

Cada uma das etapas no texto diretamente relacionadas a uma linha no cenário. Por exemplo, na linha "dados o produto X" o código de RSpec seria correspondem para o método seguinte etapa:

Given("Product $productName at $price") do |productName, price|
   pending "Need to complete implementation for accessing C# object"
 end

RSpec executará manipulação de seqüência de caracteres, permitindo que você use espaços reservados para a etapa, com o valor de espaço reservado que está sendo definido como uma variável para a etapa. Nesse caso, NomeDoProduto e preço serão armazenados na variável, permitindo que você reutilizar a mesma etapa de vários diferentes produtos e preços base.

Execute a mesma abordagem para o quando e em seguida, etapas:

  When("user requests a $amount user license") do |amount|
    pending "Need to complete implementation for accessing C# object"
  end
  When("this does not include support") do
    pending "Need to complete implementation for accessing C# object"
  end
  Then("the price should be $price") do |price|
    pending "Need to complete implementation for accessing C# object"
  End

Para a segunda etapa que não é necessário um espaço reservado porque ele é sempre a mesma ação, portanto, o bloco não tem um parâmetro. Quando você executar os testes, RSpec chamará os métodos corretos na ordem correta com base no cenário, substituindo os valores conforme definido.

Finalmente, você precisará fornecer o caminho para o arquivo de texto criado primeiro. Este é outro bloco chamado with_steps_for, fornecendo o identificador usado ao definir as etapas. O corpo chama o método execução com o caminho e o nome do arquivo onde os cenários são armazenados:

with_steps_for(:product_x) do
  run File.dirname(__FILE__) + "/pricing_scenarios_for_product_x"
end

Para executar os testes, execute IronRuby ferramenta de linha de comando (ir), passando o arquivo rubi como um parâmetro:

>ir pricing_scenarios_for_product_x_story.rb

Ao gravar as etapas, você irá notar que eu foi chamar o método pendente. Isso serve para indicar que ainda há trabalho necessário para concluir a funcionalidade. Como resultado, ao executar os testes, a saída será estado todas as tarefas pendentes (veja a Figura 1 ). Isso é ótimo para legibilidade e Noções básicas sobre o que está acontecendo — a última coisa que você deseja é para a etapa passar porque não há qualquer implementação.

Figura 1 exibição pendente métodos

Running 1 scenarios
Story: Pricing for New Product X
 As a sales administrator, I want to be able to view prices for product x, 
 so that I can provide customers with an accurate cost for their requirements.
 Scenario: Single User License for Product X without support
  Given Product X at 250 (PENDING)
  When user orders a 1 user license (PENDING)
  And this does not include support (PENDING)
  Then the price should be 250 (PENDING)
1 scenarios: 0 succeeded, 0 failed, 1 pending
Pending Steps:
1. Pricing for New Product X (Single User License for Product X without support): Product $productName at $price
2. Pricing for New Product X (Single User License for Product X without support): user orders a $amount user license
3. Pricing for New Product X (Single User License for Product X without support): this does not include support
4. Pricing for New Product X (Single User License for Product X without support): the price should be $price

Cenário de RSpec Runner interação com objetos de C#

Neste ponto, o texto, cenários e as etapas estão no lugar. No entanto, ainda não implementamos qualquer código para interagir com nosso sistema. Em meu artigo anterior, se concentra em como você pode usar a estrutura de especificação RSpec para fornecer exemplos de como os objetos de C# funcionam e verificar sua implementação como uma unidade isolada. Neste artigo, estamos em como você pode usar o RSpec texto runner para aceitação teste, enfocando verificando a pilha do aplicativo completo em vez de blocos isolados.

A primeira tarefa é fazer referência do assembly de C#. IronRuby contém true para as construções de linguagem rubi e, conseqüentemente, referências bibliotecas C# o mesmo que referência bibliotecas de rubi:

require File.dirname(__FILE__) + 
  '/CSharpAssembly/CSharpAssembly/bin/Debug/CSharpAssembly.dll'

Agora, pode começar desenvolvendo o corpo de nossos cenários que são definidos na seção anterior. Para os cenários passar, precisamos implementar a função para calcular o preço total de um pedido. Dentro do bloco determinado, você inicializar os objetos necessários para o cenário. Para nossos cenários, o único objeto necessário para ser inicializado é nesse ponto o objeto de produto. O construtor para o objeto requer o nome do produto e o preço. Ambos são obtidos do parâmetro do método, que por sua vez é obtido do próprio cenário:

  Given("Product $productName at $price") do |productName, price|
    @product = CSharpNamespace::Product.new(productName, price.to_i)
  end

Assim que tivermos os objetos iniciais, precisamos definir o estado de variável nos objetos que serão o assunto da nossos testes; isso ocorre em do bloco. Em nosso cenário, informe que um usuário foi encomendada um determinado número de licenças, e a etapa precisa refletir isso e definir o estado do objeto adequadamente:

  When("user orders a $amount user license") do |amount|
    @order = CSharpNamespace::Order.new(@product)
    @order.NumberOfLicenses = amount.to_i
  end

Finalmente, chegamos à parte onde, na verdade, verificar se a ação acima funcionou corretamente. Em nosso depois bloquear, definimos nossa as expectativas e declarações.

Aqui, nós levar a ordem é criada e verifique se o subtotal correspondem o preço que são definidas dentro de nosso cenário. Deve é um método de extensão criado dinamicamente por RSpec em todos os objetos permitindo que nós verificar a verdade de nossa instrução e se ele não é verdadeiro, em seguida, uma exceção é gerada:

  Then("the price should be $price") do |price|
    @order.Subtotal.should == price.to_i
  end

Agora, pode executado o texto usando o comando a seguir:

>ir pricing_scenarios_for_product_x_story.rb

Quando executar cada cenário, será RSpec saída os textos e cenários para o console, se um das etapas falhar, ele será realçar o problema específico e indicar o total no final:

Running 1 scenarios
Story: Pricing for New Product X
  As a sales administrator, I want to be able to view prices for product x, 
  so that I can provide customers with an accurate cost for their requirements.

  Scenario: Single User License for Product X without support

    Given Product X at 250
    When user orders a 1 user license
    And this does not include support
    Then the price should be 250 (FAILED)

1 scenarios: 0 succeeded, 1 failed, 0 pending

Se tudo funcionar com êxito, em seguida, o seguinte deve aparecer na linha de comando:

Running 1 scenarios
Story: Pricing for New Product X
 As a sales administrator, I want to be able to view prices for product x, 
 so that I can provide customers with an accurate cost for their requirements.
 Scenario: Single User License for Product X without support
  Given Product X at 250
  When user orders a 1 user license
  And this does not include support
  Then the price should be 250
1 scenarios: 1 succeeded, 0 failed, 0 pending

Nesse ponto podemos começar criar cenários adicionais, mais complexos, para incluir mais lógica de negócios. Por exemplo, talvez queira incluir um desconto sobre o preço, com o cenário procurando como este:

Scenario: Single User License for Product X with 20% discount 
and includes 2 years unlimited premium support.
   Given Product X
   When user requests a 1 user license
   And including 2 years support
   And support length is unlimited
   And support type is premium
   And with 20% discount
   Then the price should be $800

Conforme mencionado anteriormente, esses cenários usar terminologia de negócios, torná-los legível não apenas para o cliente, mas qualquer pessoa no negócio.

A interface do usuário de teste de aceitação

Nos meus exemplos, os testes de aceitação voltados para a lógica comercial e os objetos de domínio para testar se a lógica funcionou com êxito. Mas e quanto como o usuário interage com o aplicativo? Esses testes de aceitação devem ser existe Verifique se a lógica está correta do ponto de vista do usuário — e ponto de vista do usuário é a interface do usuário.

Pessoalmente, ACHO que testes de aceitação devem se concentrar na lógica de aplicativo e as seções importantes do código que decidir o que fazer. Se seu aplicativo tiver boa separação e boa separação da lógica do código de interface do usuário, isso deve fazer os testes mais fácil de implementar. Se você está testando nesse nível, os testes não sofrem de alterações na interface do usuário.

Enquanto os testes devem se concentrar exclusivamente na lógica, isso não significa que não deve ter quaisquer testes de aceitação ao redor a interface do usuário. Gosto de ter um conjunto de testes rápido (smoke test) que o principal "Feliz caminho" da interface do usuário de destino. Elas enfocam as partes do aplicativo que os usuários provavelmente mais usar com o objetivo de obter o valor a maioria da menor quantidade de testes. Se você tentar cobrir todos os caminhos possíveis e usos da interface do usuário, e se a interface do usuário for alterada (como mover para diferentes caixas de diálogo de opções), em seguida, você precisará alterar todos os testes.

Por exemplo, se você foram testando a interface do usuário para um site de comércio eletrônico, o caminho feliz seria selecione um item, adicioná-lo ao seu carrinho de compras, fazer check-out e consulte a confirmação da compra. Se esse cenário falhar, você realmente deseja saber assim que possível.

Para determinados aplicativos, dependendo a complexidade e a vida útil, convém ter mais testes de aceitação executando da interface do usuário para garantir que você tenha mais confiança na camada de interface do usuário. Testar com êxito a interface do usuário é um assunto complexo, porém, e não tenho o espaço para cobri-lo aqui. Eu recomendo a leitura meu postagens de blog em branco do Project para testar o WPF e WatiN para testar aplicativos da Web. Além disso, James McCaffrey foi gravado no Automação de interface do usuário com o Windows PowerShellem dezembro de 2007 emitir de MSDN Magazine .

Mover para frente

Neste ponto, você está pronto para iniciar a colocar IronRuby para trabalhar em seus testes de aceitação. As informações aqui devem obter começar, mas existem algumas coisas que você deve ter em mente.

Um possível problema é que não IronRuby é pronto para uso de produção ainda. Embora você pode usá-lo hoje, há está continuando desenvolvimento e refinamento de IronRuby e RSpec. Que significa correções de erros, mas também a possibilidade de alterações significativas. Também há questões sobre a velocidade de execução de RSpec. No entanto, vale a pena avaliar se os problemas superam as vantagens que você obtém de simplificar os testes.

Outra preocupação que algumas pessoas podem conter está relacionado ao alternância de contexto. Em geral, pessoas preferem escrever testes e código de produção no mesmo idioma. Embora geralmente Concordo, para testes de unidade em que você está fazendo um grande número de opções em uma base diária, não convém usar IronRuby e RSpec. No entanto, para teste de aceitação, a quantidade de alternância de contexto necessária é muito baixa. Como resultado, ACHO que você pode justificar o contexto de alternar para o aproveitar a legibilidade e a maneira mais natural de gravação a verificações e de cenários porque será fornecem benefícios enormes para o cliente e equipe de desenvolvimento.

No final, a integração de teste no processo de desenvolvimento de aceitação pode ser uma etapa extremamente positiva para uma organização de desenvolvimento. Usando técnicas, como textos e cenários para definir funcionalidade em termos de orientação do cliente, junto com testes de aceitação legível ainda automatizado, será capaz de criar uma parceria mais confiável com o cliente e fornecer consistentemente mais robusta e confiável software.

Ben Hall é um desenvolvedor/Testador do Reino Unido (UK) C# com um paixão alta segurança para o desenvolvimento de software. Ele adora escrever código. Ben funciona na Red Gate Software como um engenheiro de teste e goza explorando maneiras diferentes de software de teste, incluindo manuais e automatizadas do teste, com enfoque sobre melhores maneiras de testar diferentes tipos de aplicativos. Gustavo é um MVP de C# e mantém um blog em Blog.BenHall.me.uk