Há momentos em que nós - programadores e / ou nossos clientes - temos recursos limitados com os quais escrever a entrega esperada e os testes automatizados para essa entrega. Quando o aplicativo é pequeno o suficiente, você pode cortar atalhos e pular testes porque você se lembra (principalmente) do que acontece em outras partes do código quando você adiciona um recurso, corrige um bug ou refatora. Dito isso, nem sempre trabalharemos com aplicativos pequenos, além disso, eles tendem a ficar maiores e mais complexos com o tempo. Isso torna o teste manual difícil e muito chato.
Nos meus últimos projetos, fui forçado a trabalhar sem testes automatizados e, honestamente, foi constrangedor ter o cliente me enviando um e-mail após um push de código para dizer que o aplicativo estava quebrando em lugares onde eu nem tinha tocado no código.
Portanto, nos casos em que meu cliente não tinha orçamento ou intenção de adicionar qualquer estrutura de teste automatizado, comecei a testar a funcionalidade básica de todo o site enviando uma solicitação HTTP para cada página individual, analisando os cabeçalhos de resposta e procurando o '200' resposta. Parece claro e simples, mas há muito que você pode fazer para garantir a fidelidade sem realmente ter que escrever nenhum teste, unidade, funcional ou integração.
No desenvolvimento web, os testes automatizados são compostos por três tipos principais de teste: testes de unidade, testes funcionais e testes de integração. Freqüentemente, combinamos testes de unidade com testes funcionais e de integração para garantir que tudo funcione perfeitamente como um aplicativo completo. Quando esses testes são executados em uníssono, ou sequencialmente (de preferência com um único comando ou clique), passamos a chamá-los de testes automatizados, unitários ou não.
Em grande parte, o objetivo desses testes (pelo menos em desenvolvimento web) é garantir que todas as páginas do aplicativo sejam renderizadas sem problemas, livres de erros fatais (interrupção do aplicativo) ou bugs.
gerador de site estático node js
O teste de unidade é um processo de desenvolvimento de software no qual as menores partes do código - unidades - são testadas de forma independente para operação correta. Aqui está um exemplo em Ruby:
test “should return active users” do active_user = create(:user, active: true) non_active_user = create(:user, active: false) result = User.active assert_equal result, [active_user] end
O teste funcional é uma técnica usada para verificar os recursos e a funcionalidade do sistema ou software, projetado para cobrir todos os cenários de interação do usuário, incluindo caminhos de falha e casos de limite.
Nota: todos os nossos exemplos estão em Ruby.
test 'should get index' do get :index assert_response :success assert_not_nil assigns(:object) end
Uma vez que os módulos são testados por unidade, eles são integrados um a um, sequencialmente, para verificar o comportamento combinacional e para validar se os requisitos foram implementados corretamente.
test 'login and browse site' do # login via https https! get '/login' assert_response :success post_via_redirect '/login', username: users(:david).username, password: users(:david).password assert_equal '/welcome', path assert_equal 'Welcome david!', flash[:notice] https!(false) get '/articles/all' assert_response :success assert assigns(:articles) end
O teste é amplamente aceito na indústria e faz sentido; bons testes permitem que você:
Eu poderia ficar falando sem parar sobre como os testes são incríveis e como eles mudaram o mundo e blá blá blá, mas você entendeu. Conceitualmente, os testes são fantásticos.
Relacionado: Testes de unidade, como escrever código testável e por que é importanteEmbora haja méritos em todos os três tipos de teste, eles não são escritos na maioria dos projetos. Por quê? Bem, deixe-me decifrar:
Todo mundo tem prazos, e escrever novos testes pode atrapalhar o cumprimento de um. Pode levar tempo e meio (ou mais) para escrever um aplicativo e seus respectivos testes. Agora, alguns de vocês não concordam com isso, citando o tempo economizado em última análise, mas não acho que seja esse o caso e explicarei o porquê em ‘Diferença de opinião’.
Frequentemente, o cliente não entende realmente o que é teste ou por que ele tem valor para o aplicativo. Os clientes tendem a se preocupar mais com a entrega rápida do produto e, portanto, consideram os testes programáticos contraproducentes.
Ou pode ser tão simples quanto o cliente não ter orçamento para pagar pelo tempo extra necessário para implementar esses testes.
Existe uma tribo considerável de desenvolvedores no mundo real que não sabe que existe o teste. Em cada conferência, encontro, show, (mesmo em meus sonhos), encontro desenvolvedores que não sabem escrever testes, não sabem o que testar, não sabem como configurar a estrutura para testes, e assim em. O teste não é exatamente ensinado nas escolas e pode ser um incômodo configurar / aprender a estrutura para colocá-los em funcionamento. Então, sim, há uma barreira definitiva para a entrada.
Escrever testes pode ser opressor para programadores novos e experientes, mesmo para os tipos de gênios que mudam o mundo, e ainda por cima, escrever testes não é emocionante. Alguém pode pensar: “Por que eu deveria me envolver em uma ocupação pouco excitante quando poderia implementar um recurso importante com resultados que impressionarão meu cliente?” É um argumento difícil.
Por último, mas não menos importante, é difícil escrever testes e os alunos de ciência da computação não são treinados para isso.
Ah, e refatorar com testes de unidade não é divertido.
Em minha opinião, o teste de unidade faz sentido para lógica algorítmica, mas não tanto para coordenar código vivo.
um bom design de banco de dados não inclui:
As pessoas afirmam que, embora você esteja investindo tempo extra antecipadamente na escrita de testes, você economiza horas depois ao depurar ou alterar o código. Eu discordo e ofereço uma pergunta: seu código está estático ou está sempre mudando?
Para a maioria de nós, está sempre mudando. Se você está escrevendo um software de sucesso, está sempre adicionando recursos, alterando os existentes, removendo-os, comendo-os, o que for, e para acomodar essas alterações, você deve continuar alterando seus testes, e mudar seus testes leva tempo.
Ninguém vai argumentar que a falta de qualquer tipo de teste é o pior caso possível. Depois de fazer alterações em seu código, você precisa confirmar se ele realmente funciona. Muitos programadores tentam testar manualmente o básico: a página é renderizada no navegador? O formulário está sendo enviado? O conteúdo correto está sendo exibido? E assim por diante, mas na minha opinião, isso é bárbaro, ineficiente e trabalhoso.
O objetivo de testar um aplicativo da web, seja manualmente ou automatizado, é confirmar se qualquer página é renderizada no navegador do usuário sem erros fatais e que mostra seu conteúdo corretamente. Uma maneira (e na maioria dos casos, uma maneira mais fácil) de fazer isso é enviar solicitações HTTP aos endpoints do aplicativo e analisar a resposta. O código de resposta informa se a página foi entregue com sucesso. É fácil testar o conteúdo analisando o corpo da resposta da solicitação HTTP e procurando por correspondências de string de texto específicas ou, você pode ser um passo mais avançado e usar bibliotecas de scraping da web, como Nokogiri .
Se alguns endpoints exigem um login de usuário, você pode usar bibliotecas projetadas para automatizar interações (ideal ao fazer testes de integração), como mecanizar para fazer o login ou clicar em determinados links. Na verdade, no quadro geral dos testes automatizados, isso se parece muito com integração ou teste funcional (dependendo de como você os usa), mas é muito mais rápido de escrever e pode ser incluído em um projeto existente ou adicionado a um novo , com menos esforço do que configurar toda a estrutura de teste. Spot on!
Relacionado: Contrate os melhores 3% dos engenheiros autônomos de controle de qualidade.Os casos extremos apresentam outro problema ao lidar com grandes bancos de dados com uma ampla gama de valores; testar se nosso aplicativo está funcionando sem problemas em todos os conjuntos de dados antecipados pode ser assustador.
Uma maneira de fazer isso é antecipar todos os casos extremos (o que não é apenas difícil, mas muitas vezes impossível) e escrever um teste para cada um. Isso poderia facilmente se tornar centenas de linhas de código (imagine o horror) e difícil de manter. Ainda assim, com solicitações HTTP e apenas uma linha de código, você pode testar esses casos extremos diretamente nos dados de produção, baixados localmente em sua máquina de desenvolvimento ou em um servidor de teste.
É claro que essa técnica de teste não é uma solução mágica e tem muitas falhas, como qualquer outro método, mas acho esses tipos de testes mais rápidos e fáceis de escrever e modificar.
Como já estabelecemos que escrever código sem nenhum tipo de teste de acompanhamento não é uma boa ideia, meu teste básico para um aplicativo inteiro é enviar solicitações HTTP para todas as suas páginas localmente e analisar os cabeçalhos de resposta para um 200
código (ou desejado).
Por exemplo, se fossemos escrever os testes acima (aqueles que procuram por conteúdo específico e um erro fatal) com uma solicitação HTTP (em Ruby), seria algo assim:
por que o desenvolvimento do Android é tão complicado
# testing for fatal error http_code = `curl -X #{route[:method]} -s -o /dev/null -w '%{http_code}' #{Rails.application.routes.url_helpers.articles_url(host: 'localhost', port: 3000) }` if http_code !~ /200/ return “articles_url returned with #{http_code} http code.” end # testing for content active_user = create(:user, name: “user1”, active: true) non_active_user = create(:user, name: “user2”, active: false) content = `curl #{Rails.application.routes.url_helpers.active_user_url(host: 'localhost', port: 3000) }` if content !~ /#{active_user.name}/ return “Content mismatch active user #{active_user.name} not found in text body” #You can customise message to your liking end if content =~ /#{non_active_user.name}/ return “Content mismatch non active user #{active_user.name} found in text body” #You can customise message to your liking end
A linha curl -X #{route[:method]} -s -o /dev/null -w '%{http_code}' #{Rails.application.routes.url_helpers.articles_url(host: 'localhost', port: 3000) }
cobre muitos casos de teste; qualquer método que gerar um erro na página do artigo será detectado aqui, portanto, cobre efetivamente centenas de linhas de código em um teste.
A segunda parte, que detecta especificamente o erro de conteúdo, pode ser usada várias vezes para verificar o conteúdo de uma página. (Solicitações mais complexas podem ser tratadas usando mechanize
, mas isso está além do escopo deste blog.)
Agora, nos casos em que você deseja testar se uma página específica funciona em um conjunto grande e variado de valores de banco de dados (por exemplo, seu modelo de página de artigo está funcionando para todos os artigos no banco de dados de produção), você pode fazer:
ids = Article.all.select { |post| `curl -s -o /dev/null -w “%{http_code}” #{Rails.application.routes.url_helpers.article_url(post, host: 'localhost', port: 3000) }`.to_i != 200).map(&:id) return ids
Isso retornará uma matriz de IDs de todos os artigos no banco de dados que não foram renderizados, então agora você pode ir manualmente para a página do artigo específico e verificar o problema.
Agora, eu entendo que esta forma de teste pode não funcionar em certos casos, como testar um script autônomo ou enviar um e-mail, e é inegavelmente mais lento do que os testes de unidade porque estamos fazendo chamadas diretas para um endpoint para cada teste, mas quando você não pode ter testes de unidade, ou testes funcionais, ou ambos, isso é melhor do que nada.
em qual linguagem os x está escrito
Como você faria para estruturar esses testes? Com projetos pequenos e não complexos, você pode escrever todos os seus testes em um arquivo e executar esse arquivo todas as vezes antes de confirmar suas alterações, mas a maioria dos projetos exigirá um conjunto de testes.
Normalmente escrevo de dois a três testes por endpoint, dependendo do que estou testando. Você também pode tentar testar o conteúdo individual (semelhante ao teste de unidade), mas acho que seria redundante e lento, pois você fará uma chamada HTTP para cada unidade. Mas, por outro lado, eles serão mais limpos e fáceis de entender.
Eu recomendo colocar seus testes em sua pasta de teste regular com cada endpoint principal tendo seu próprio arquivo (em Rails, por exemplo, cada modelo / controlador teria um arquivo cada), e este arquivo pode ser dividido em três partes de acordo com o que nós estão testando. Costumo fazer pelo menos três testes:
Verifique se a página retorna sem erros fatais.
Observe como fiz uma lista de todos os pontos de extremidade para Post
e iterado sobre ele para verificar se cada página é renderizada sem nenhum erro. Supondo que tudo tenha corrido bem e todas as páginas renderizadas, você verá algo assim no terminal: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of failed url(s) -- []
Se alguma página não for renderizada, você verá algo assim (neste exemplo, o posts/index page
tem erro e, portanto, não é renderizado): ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of failed url(s) -- [{:url=>”posts_url”, :params=>[], :method=>”GET”, :http_code=>”500”}]
Confirme se todo o conteúdo esperado está lá:
exemplo de programação linear inteira mista
Se todo o conteúdo que esperamos for encontrado na página, o resultado será semelhante a este (neste exemplo, certificamo-nos de que posts/:id
tem um título de postagem, uma descrição e um status): ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of content(s) not found on Post#show page with post id: 1 -- []
Se algum conteúdo esperado não for encontrado na página (aqui esperamos que a página mostre o status da postagem - 'Ativo' se a postagem estiver ativa, 'Desativado' se a postagem estiver desabilitada), o resultado será assim: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of content(s) not found on Post#show page with post id: 1 -- [“Active”]
Verifique se a página é renderizada em todos os conjuntos de dados (se houver):
Se todas as páginas forem renderizadas sem nenhum erro, obteremos uma lista vazia: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of post(s) with error in rendering -- []
Se o conteúdo de alguns dos registros apresentar um problema de renderização (neste exemplo, as páginas com ID 2 e 5 apresentam um erro), o resultado será o seguinte: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of post(s) with error on rendering -- [2,5]
Se você quiser mexer no código de demonstração acima, aqui está o meu projeto github .
O teste de solicitação HTTP pode ser sua melhor aposta se:
O teste tradicional é ideal quando:
Obrigado por ler o artigo; Agora você deve ter um método de teste que pode ser padronizado, um método com o qual pode contar quando estiver sem tempo.
Relacionado: