Testes unitários de código JavaScript: estratégias, bibliotecas, ferramentas. Testes JavaScript e sua automação Testes, finalidade e uso de javascript

A criação de casos de teste eficazes pode ser extremamente importante para projetos grandes, nos quais o comportamento de partes da aplicação pode mudar por vários motivos. Talvez o problema mais comum seja quando um grande grupo de desenvolvedores está trabalhando no mesmo módulo ou em módulos relacionados. Isto pode levar a mudanças não planejadas no comportamento de funções escritas por outros programadores. Ou trabalhar com prazos apertados leva a alterações não intencionais em partes críticas do aplicativo.

Testar um aplicativo da web geralmente envolve avaliar visualmente os elementos da página e avaliar empiricamente o desempenho da funcionalidade. Em outras palavras, ao percorrer seções e realizar ações em elementos dinâmicos.

Com o tempo, o projeto é preenchido com novas funcionalidades, o que alonga e complica o processo de verificação de seu funcionamento. Para automação, testes unitários são usados.

Existem 2 abordagens para construir cenários de teste:

  • Teste de caixa branca – a escrita de testes é baseada na implementação de funcionalidades. Aqueles. Verificamos usando os mesmos algoritmos nos quais se baseia o trabalho dos módulos do nosso sistema. Esta abordagem não garante o correto funcionamento do sistema como um todo.
  • Teste Blackbox – a criação do script é baseada nas especificações e requisitos do sistema. Dessa forma, você pode verificar a exatidão dos resultados de todo o aplicativo, mas essa abordagem não permite detectar erros pequenos e raros.
O que testar

Pode parecer que vale a pena testar cada recurso que você implementa. Isto não é inteiramente verdade. Escrever testes ocupa o tempo do desenvolvedor, portanto, para otimizar o processo de criação de uma aplicação, vale a pena preparar testes apenas para funções complexas e críticas, ou para aquelas funções que dependem dos resultados de outros módulos do sistema. Cubra a lógica ambígua com testes que podem causar erros. Também vale a pena criar testes para aquelas seções de código que estão planejadas para serem otimizadas no futuro, para que após o processo de otimização você possa verificar se elas foram executadas corretamente.

Em geral, é extremamente importante avaliar os custos dos testes em relação ao aperto dos prazos de desenvolvimento. Claro, se você não estiver limitado no tempo, poderá permitir que cada função seja coberta por testes. Mas, como regra, o desenvolvimento é realizado sob forte pressão de tempo, portanto, a tarefa de um analista ou de um desenvolvedor experiente é entender onde os testes são necessários. Além disso, escrever testes aumenta o custo do projeto.

Assim, podemos formular 3 casos em que se justifica a utilização de testes unitários:

1) Se os testes permitirem identificar erros mais rapidamente do que na busca normal.

2) Reduza o tempo de depuração

3) Permite testar códigos alterados com frequência.

Dos 3 componentes principais do frontend (HTML, CSS, JavaScript), talvez apenas o código JavaScript precise ser testado. CSS é testado puramente visualmente, onde o desenvolvedor/testador/cliente visualiza a GUI em diferentes navegadores. A marcação HTML é verificada usando o mesmo método.

Como testar

Ao construir cenários de teste, você deve ser guiado pelos seguintes princípios:

  • Seus testes devem ser tão simples quanto possível.
  • Então haverá uma maior probabilidade de que os resultados de sua implementação sejam influenciados pelo próprio bug que você está tentando repetir.
  • Decomponha grandes testes unitários.
  • É melhor encontrar o local específico do erro.
  • Torne os testes independentes.
O resultado de um teste não deve de forma alguma depender dos resultados de outro.

Os resultados dos testes devem ser totalmente repetíveis e esperados. Cada vez que você executar o teste novamente, o resultado deverá ser o mesmo da última vez.

Para qualquer erro na execução da aplicação, um script de teste deverá ser criado. Desta forma você terá certeza de que o bug foi realmente corrigido e não aparece para os usuários.

O que testar< int; i ++) { result = result * 2; } return result; } window.returnFunc = function() { return "ok"; } })();

Existem várias bibliotecas para código js de teste de unidade. Talvez o mais comum seja o QUnit. Para realizar testes unitários usando esta biblioteca, precisaremos criar uma “sandbox” - uma página HTML simples na qual a biblioteca para teste, o código que precisa ser testado e os próprios testes serão conectados.

Test("stepen()", function() ( equal(stepen(2), 4, "2^2 - método igual"); ok(stepen(3) === 8, "2^3 - método ok" ); deepEqual(stepen(5), 32, "2^5 - método deepEqual"); asyncTest("returnFunc()", function() ( setTimeout(function() ( equal(returnFunc(), "ok", "Teste de função assíncrona"); start(); ), 1000); ));

Como você pode ver, QUnit suporta 3 funções para comparar os resultados da execução do código com os esperados:

  • ok() – considera o teste bem sucedido se o resultado retornado = verdadeiro
  • equal() – compara o resultado com o esperado
  • deepEqual() – compara o resultado com o esperado, verificando seu tipo

Resultado da execução:

Como você pode ver, a biblioteca QUnit testa código para vários navegadores ao mesmo tempo.

Existem várias outras bibliotecas de testes de unidade disponíveis. No entanto, o conceito de construção de cenários de teste neles é o mesmo, portanto, depois de entender um, não será difícil mudar para outro.

Importante lembrar

Uma característica do código js moderno é sua execução assíncrona. As bibliotecas de teste geralmente têm a capacidade de realizar testes assíncronos. Mas, por exemplo, se você estiver tentando testar uma função que, digamos, envia uma solicitação get para o backend e retorna uma resposta dele, então para realizar testes você terá que parar o thread com a função stop(), iniciar o função em teste e, em seguida, reinicie o thread com o método start(), "envolvendo-o" em setTimeout(). Aqueles. você deve definir um determinado período de tempo durante o qual a função deve concluir a execução. Você precisa escolher cuidadosamente a duração deste segmento. Por um lado longo trabalho O método pode ser um recurso ou mesmo uma necessidade para uma implementação específica da funcionalidade do aplicativo ou um comportamento incorreto.

Testando aplicativos de backbone

Para obter um exemplo de teste de aplicativos escritos usando Backbone.js, usaremos o projeto descrito em.

Você pode usar testes de unidade para verificar:

  • Criação correta de modelos e controladores
  • Correção dos dados nos modelos
  • Execução de métodos do controlador (para isso devem retornar um resultado)
  • Sucesso no carregamento de visualizações

Código de teste:

Test("Backbone.js", function() ( ok(sample, "Verificação de namespace"); ok(sample.routers.app, "Verificação de roteador"); ok(sample.core.pageManager.open("chat") , "Teste de abertura de página (chamada de método do controlador)") ok(sample.core.state, "Verificação de modelo"); equal(sample.core.state.get("content"), "sintel", "Dados do modelo obtêm teste " "); stop(); ok(function() ( $.ajax(( url: "app/templates/about.tpl", dataType: "texto")).done(function(data) ( self.$el .html(data); return data ))), "Verificação de carregamento do modelo");

Resultado de trabalhar com erros de teste:

Automação de execução de teste

Normalmente, a implantação de um aplicativo é uma tarefa que precisa ser executada com bastante frequência durante o desenvolvimento intensivo. Portanto, esta operação geralmente é automatizada. Em nosso trabalho utilizamos Jenkins, uma ferramenta de integração contínua. A ideia é combinar a implantação via Jenkins com testes automatizados.

Os testes QUnit são executados no navegador. Phantomjs, software que emula o funcionamento de um navegador, nos ajudará a contornar esse recurso. Os desenvolvedores do phantomjs já forneceram um script para executar testes QUnit, mas para operação correta Tive que modificar um pouco.

/** * Aguarde até que a condição de teste seja verdadeira ou ocorra um tempo limite.< Default Max Timout is 3s start = new Date().getTime(), condition = false, interval = setInterval(function() { if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) { // If not time-out yet and condition not yet fulfilled condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code } else { if(!condition) { // If condition still not fulfilled // (timeout but condition is "false") console.log(""waitFor()" timeout"); phantom.exit(1); } else { // Condition fulfilled (timeout and/or condition is //"true") console.log(""waitFor()" finished in " + (new Date().getTime() - start) + "ms."); typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it"s supposed to do once the // condition is fulfilled clearInterval(interval); //< Stop this interval } } }, 100); // repeat check every 250ms }; }; if (phantom.args.length === 0 || phantom.args.length >2) console.log("Uso: URL run-qunit.js");

fantasma.exit(); ) var página = nova página da Web(); // Roteie chamadas "console.log()" de dentro do contexto da página // para o contexto Phantom principal (ou seja, "this" atual) page.onConsoleMessage = function(msg) ( console.log(msg); ); page.open(phantom.args, function(status)( if (status !== "sucesso") ( console.log("Não foi possível acessar a rede"); phantom.exit(); ) else ( waitFor(function() ( return page.evaluate(function())( var el = document.getElementById("qunit-testresult"); if (el && el.innerText.match("concluído")) ( return true; ) return false; )) ; ), function())( var failedNum = page.evaluate(function())( var el = document.getElementById("qunit-testresult"); console.log(el.innerText); try ( return document.getElementsByClassName( "falha" ).innerHTML.length ) catch (e) ( return 0; ) return phantom.exit((parseInt(failedNum, 10) ? 1: ));

Para enviar mensagens sobre resultados para o console, você precisa adicionar uma função de registro ao script de teste.

O teste é parte integrante do ciclo de desenvolvimento de software. As equipes de desenvolvimento iniciantes muitas vezes subestimam seu papel e verificam a funcionalidade do aplicativo à moda antiga - “funciona, ok”. Mais cedo ou mais tarde, esta estratégia falha e o bug tracker começa a ser sobrecarregado por um incontável exército de tarefas. Para evitar cair nessa armadilha, recomendo de uma vez por todas entender as nuances do teste do código JavaScript.

JavaScript não é mais bolo! Provavelmente não preciso explicar para você que hoje o JavaScript não é apenas uma linguagem para apimentar a aparência de uma aplicação. Mas vou explicar e fazer assim mesmo pequena introdução

, porque então eles vão me pagar ainda mais dinheiro! 🙂 Portanto, os tempos em que o JavaScript era usado para piadas ou para fazer menus acabaram irrevogavelmente. Agora é uma linguagem independente que funciona igualmente bem tanto no cliente quanto no servidor. A função do JavaScript aumentou significativamente, o que significa que, ao escrever código, você não deve ter vergonha de usar práticas comprovadas em outras linguagens de programação.

O que quero dizer com práticas e paradigmas? Claro, o padrão de arquitetura MVC (model view controller) e os padrões de organização de código. Seguindo esses truques simples, você será capaz de escrever um código melhor que não apenas será fácil de manter, mas também poderá ser testado automaticamente.
  • O teste deve ser o mais simples possível. Quanto mais complexo o teste, maior a probabilidade de cometer erros.
  • Os testes precisam ser agrupados em módulos para facilitar a localização de erros posteriormente e poder testar partes específicas do aplicativo.
  • Cada teste deve ser independente de outros testes.
  • Sempre escreva um teste separado sempre que encontrar bugs.
Erro da maioria dos testadores

Não é nenhum segredo que o método de teste mais popular sempre foi um exame oftalmológico banal. Sua essência é simples ao ponto da desgraça - você escreve alguns milhares de linhas de código, resolve o problema e inicia sua criação. Brinquei, cliquei - tudo parece estar funcionando, você pode fazer upload para o servidor de produção. Tudo é extremamente simples, e com a devida atenção do desenvolvedor (de preferência um indivíduo apelidado de “testador”), você pode confiar no correto funcionamento do aplicativo.

Na prática, tudo acontece de forma um pouco diferente. Via de regra, não existe um testador separado. O próprio desenvolvedor tenta verificar a funcionalidade do programa executando a sequência de ações especificadas nas especificações técnicas. Forjamentos de código mais avançados automatizam esse tipo de teste de integração usando ferramentas como Selenium.

Assim, o programador tem a oportunidade de detectar apenas os mais erros grosseiros. Infelizmente, ações “estúpidas” e “não intencionais” dos usuários, bem como movimentos astutos na lógica de negócios, permanecem nos bastidores em 99% dos casos.

A presença de um testador separado também resolve o problema parcialmente e até certo tempo. Mesmo se ignorarmos a atenção de seu sapador aos detalhes, a qualidade de seus testes tenderá a zero à medida que a aplicação crescer. Deixe-me dar um exemplo da prática.

Um dia fui designado para desenvolver um pequeno programa. Em termos de funcionalidade, o projeto lembrava um CRM simples, que implementei no menor tempo possível. Recebida a devida recompensa, entreguei todas as fontes ao cliente e esqueci o projeto por oito meses. Então a diversão começou. O cliente decidiu expandir seriamente a funcionalidade do programa e me ligou pedindo ajuda. Naturalmente, peguei-o e comecei a esculpir função após função... No início não foi difícil, mas quando se tratou da integração geral da funcionalidade, um enxame de insetos correu em minha direção. Pedaços de código começaram a entrar em conflito e tivemos que gastar muito tempo resolvendo conflitos. “Bem, por que você não percebeu que havia problemas com sua inscrição?” - perguntarão os leitores. Vou responder: lancei, mas devido ao fato do aplicativo ter crescido, simplesmente não tive tempo e nervos para testar em massa todas as funcionalidades. Limitei-me a testar apenas funções individuais e paguei muito por isso. Moral da história: “Pense nos testes como parte integrante do desenvolvimento.”

Os testes unitários são como uma bala de prata

O teste de unidade é a melhor maneira de acalmar seus nervos e aumentar as garantias de funcionalidade de partes individuais do aplicativo. Se você nunca encontrou esta fera terrível, explicarei brevemente. Os testes de unidade permitem automatizar o processo de teste e testar todos os recursos do seu aplicativo.

Após a conclusão do desenvolvimento novo recurso(é possível escrever testes antes do início do desenvolvimento) o desenvolvedor escreve um código especial para testar seu código. Precisa simular diferentes situações e retornar valores. Por exemplo, escrevemos uma função para cortar espaços (trim). Para testar o seu desempenho, devemos preparar vários testes que nos permitirão afirmar que:

  • ao passar a string "string" obteremos "string" como saída;
  • ao transmitir os termos “linha 9”, receberemos “linha 9” na saída;

Também podemos adicionar testes para outros parâmetros de entrada (por exemplo, substituindo o caractere de espaço por uma tabulação). Em geral, quanto melhor cobrirmos o código com testes e quanto mais fornecermos possíveis opções negativas, maiores serão as chances de que no momento mais crucial ainda haja um pouco de cabelo na cabeça.

No mundo JS, os testes geralmente são descritos usando frameworks especializados. Eles têm tudo que você precisa para isso, além de algumas ferramentas para sistematizar relatórios sobre o andamento dos testes.

Testes!= código extra

Os desenvolvedores que não usam testes unitários gostam de argumentar que os testes unitários exigem escrever e manter código adicional. Eles dizem que os prazos em projetos reais costumam ser apertados e simplesmente não é possível escrever código adicional.

Quando não há tempo para testes

Se não houver tempo, não adianta escrever testes para funções simples(pegue o mesmo trim() do exemplo do artigo), é melhor focar nas seções mais críticas do código. A mesma regra deve ser seguida ao escrever código alterado com frequência. As especificações técnicas de um projeto ativo mudam frequentemente e algumas funções precisam ser constantemente atualizadas. Tais mudanças podem implicar momentos desagradáveis- o código modificado funciona bem com novos dados, mas não digere organicamente os dados antigos. Para não ocorrer uma falha aqui, é melhor verificar essas funções imediatamente. Lembre-se de uma regra simples: não há tempo para cobrir todo o código com testes - cubra a parte mais importante dele.


Concordo com os prazos apertados, mas estou disposto a discutir sobre o código extra. Por um lado, sim - os testes requerem código adicional e, portanto, tempo para escrevê-lo. Por outro lado, esse código desempenha o papel de airbags em um carro e certamente se pagará à medida que a aplicação crescer.
  • Test-Driven JavaScript Development, de Cristian Johansen (goo.gl/mE6Is), é um dos poucos livros que analisa o JavaScript da perspectiva da escrita de testes.
  • John Resing, Beer Bibo “Secrets of the JavaScript Ninja” (goo.gl/xquDkJ) é um bom livro que será útil principalmente para desenvolvedores JS com um nível médio de treinamento. O livro discute em detalhes as questões de escrever código eficaz para vários navegadores, as nuances do processamento de eventos e muitas outras vantagens.
  • David Flanagan JavaScript. Guia Completo"(goo.gl/rZjjk) - o livro foi reimpresso seis vezes e cada lançamento se torna um best-seller. Na verdade, este é o mais guia detalhado em JavaScript, que todo desenvolvedor JS deve ler pelo menos uma vez.
  • PhantomJS + JSCoverage + QUnit ou testes de unidade JS de console com cálculo de cobertura (goo.gl/FyQ38) - o autor do artigo demonstra o uso de vários pacotes listados para coletar estatísticas e calcular a porcentagem de cobertura de código por testes.
  • Exemplos úteis de uso do PhantomJS - a página apresenta um grande número de aplicativos de combate do PhantomJS.

Quando você não tiver tempo e estiver atormentado pela vontade de desistir de fazer provas, pense três vezes. Talvez, neste caso, fosse mais apropriado cobrir apenas as seções mais complicadas do código com testes, em vez de abandonar completamente os testes. Pense sempre com os olhos postos no futuro, como se em um mês o seu programa pudesse crescer a proporções sem precedentes. Nem todo código é testado

Por que digo que você precisa pensar em testar antes de escrever o código principal? Sim, porque o código que inicialmente deveria ser coberto pelos testes unitários é escrito em um estilo ligeiramente diferente. Nem todo código pode ser testado. Código em que lógica e representações são misturadas, e até mesmo amontoados sabe-se lá onde, é impossível de ser testado adequadamente. Aqui eu sempre aconselho você a seguir algumas regras simples:

  • Não há necessidade de escrever ótimas funções. Cada função deve resolver um problema, e não 100.500 situações possíveis. Por exemplo, não há necessidade de colocar o código de envio de dados ao servidor na função responsável por prepará-los.
  • Uma função que consiste em mais de dez linhas de código é provavelmente uma função incorreta.
  • Lógica e apresentação nunca devem andar juntas.
QUnit - um clássico dos criadores do jQuery

QUnit é especialmente popular entre desenvolvedores de JavaScript. Em primeiro lugar, está bem documentado e fácil de usar e, em segundo lugar, foi criado pelos autores do jQuery. A biblioteca é adequada para testar código criado em base jQuery e JavaScript nativo.


Download versão mais recente Você pode usar o QUnit no site oficial. A biblioteca vem como um único arquivo JS e CSS. Vamos supor que você descobriu como carregar os componentes necessários e, se sim, é hora de escrever um teste de teste. Não vamos muito longe e tentaremos testar a função trim().

Para demonstrar os testes, criei um projeto simples com a seguinte estrutura:

  • index.html - o arquivo principal que exibirá os resultados dos testes;
  • qunit-1.12.0.js - arquivo de biblioteca QUnit;
  • example.js - um arquivo contendo código para teste (no nosso caso, uma descrição da função trim());
  • test.js - arquivo com testes;
  • qunit-1.12.0.css - estilos para criar um relatório com testes.

O conteúdo dos arquivos index.html e test.js é apresentado nas Listagens 1 e 2. Estamos mais interessados ​​na segunda listagem, que contém a declaração da função em teste (trim()) e o código de teste para verificar sua funcionalidade. Observe que a função trim() em si pode estar localizada em qualquer lugar. Coloquei-a na segunda listagem apenas para economizar espaço na revista.
Agora vamos dar uma olhada nos testes em si. A biblioteca QUnit.js nos oferece vários métodos:

  • test() - wrapper para descrever o teste;
  • ok() - a afirmação permite verificar a veracidade do primeiro parâmetro. Em nosso exemplo, passo uma chamada para a função trim() que definimos e comparo com o valor que espero receber. Se a condição for verdadeira, o teste foi aprovado;
  • equal() - o método permite verificar a igualdade do primeiro e do segundo parâmetros. Observe imediatamente que este método executa uma verificação fraca, portanto é adequado apenas para grandezas escalares;
  • notEqual() é o oposto de igual(). Executado se o primeiro valor não for igual ao segundo;
  • strictEqual() - semelhante a equal() com apenas uma diferença - utiliza verificação estrita (ou seja, também verifica o tipo de dados);
  • notStrictEqual() - o método é o oposto de strictEqual();
  • deepEqual() – método para asserções recursivas, usado para primitivas, arrays, objetos;
  • notDeepEqual() - o método é o oposto de deepEqual();
  • raises() é uma instrução para testar funções de retorno de chamada que lançam uma exceção.

Na segunda listagem mostrei claramente como aplicar esses métodos na prática. Se você executar o exemplo de teste neste formulário, todos os testes serão aprovados com êxito (veja a figura correspondente). Para ver a diferença entre os testes que passaram e os que falharam, modifiquei ligeiramente o código de um teste. Adicionei deliberadamente um resultado errado à linha de teste usando strictEqual() (veja a figura correspondente).

Listagem 1. Conteúdo do arquivo index.html Testando com QUnit

Parece que resolvemos testar funções simples. De qualquer forma, não tenho mais nada a acrescentar. Em seguida, você precisa pegar o código real e tentar escrever testes sozinho. Vejamos outra tarefa frequentemente encontrada por desenvolvedores de JavaScript - testar funções assíncronas. Um aplicativo repleto de código JavaScript interage 99% com o lado do servidor usando AJAX. Você também não pode deixar esse código desmarcado, mas escrever testes será um pouco diferente. Vejamos um exemplo:

AsyncTest("myAsyncFunc()", function () ( setTimeout(function () ( ok(myAsyncFunc() == true, "Dados transferidos com sucesso"); start(); ), 500); ));

A principal diferença entre este exemplo e o anterior é que asyncTest() é usado em vez do wrapper test(), afirmando diretamente que estou interessado em testes assíncronos. Em seguida, executo um tempo limite de 500 milissegundos. Durante esse tempo, a função myAsyncFunc() deverá transferir dados para o servidor de teste e, se tudo estiver OK, retornar verdadeiro. É aqui que chega o momento mais interessante. Quando asyncTest() é chamado, o thread de execução é interrompido e deve ser executado quando o teste for concluído. Para controlar o fluxo de execução, QUnit possui os métodos start() e stop().


Testar funções assíncronas é bastante fácil com a biblioteca QUnit. O último exemplo que gostaria de ver envolve escrever um teste que executa múltiplas verificações assíncronas. A principal questão que surge em tais problemas é o local ideal para iniciar o thread de execução. O documento oficial sugere usar algo como

AsyncTest("myAsyncFunc()", function () ( expect(3); // Aqui fazemos três verificações ok(myAsyncFunc(), "Tornando o mundo um lugar melhor 1"); ok(myAsyncFunc(), "Tornando o mundo um lugar melhor 2") ; ok(myAsyncFunc(), "Tornando o mundo um lugar melhor 3"); setTimeout(function () ( start (), 3000 ));

Teste para ações personalizadas

Você deve sempre lembrar que muitas coisas da interface são escritas em JavaScript. Por exemplo, um usuário clica em um cafetão e algo deve acontecer em resposta ao seu clique. Há uma grande quantidade desse código de “interface” em projetos e também precisa ser coberto com testes. Vamos ver como podemos simular o pressionamento de tecla de um usuário e escrever um teste separado para esta ação. Vamos imaginar que temos uma determinada função que registra as teclas pressionadas. Forneci seu código na terceira listagem.

Listagem 3. Função de registro de pressionamentos de teclas KeyLogger(target) ( if (!(this instanceof KeyLogger)) ( return new KeyLogger(target); ) this.target = target; this.log = ; var self = this; this.target. off ("keydown").on("keydown", function(event) ( self.log.push(event.keyCode); ));

Agora vamos tentar testar esta função. Primeiramente, no corpo do teste precisamos emular a tecla pressionada. A maneira mais fácil de fazer isso é com a biblioteca jQuery, que permite criar um evento em algumas linhas de código (consulte a Listagem 4).

Listagem 4. Código de teste para KeyLogger test("Key logging test", function () ( var event, $doc = $(document), keys = KeyLogger($doc); event = $.Event("keydown"); event .keyCode = 9; $doc.trigger(event); equal(keys.log.length, 1, "Chave registrada"); equal(keys.log, 9, "Tecla pressionada com código 9" ));

Logo no início da listagem de teste, preparo um evento para emular o pressionamento de uma tecla - “keydown”. Estaremos interessados ​​​​em pressionar a tecla Tab (código 9). Em seguida, usando o método trigger(), despacho o evento preparado, após o qual posso iniciar os testes. Primeiro, verificamos o quadro geral - se uma tecla foi pressionada e, em seguida, seu código.

DOM sob o disfarce de testes

Como o Qunit.js permite testar as ações do usuário, escrever testes para o DOM também não deve ser um problema. Isto é realmente verdade, e o exemplo abaixo confirmará minhas palavras. Não vou comentar, basta olhar o código e tudo ficará claro:

Teste("Adicionar novo elemento div", function () ( var $fixture = $("#qunit-fixture"); $fixture.append("Esta é uma nova div"); equal($("div", $fixture).length, 1 , "Nova div adicionada com sucesso!" ));

PhantomJS - executando testes no console

Escrever testes usando a biblioteca QUnit.js é conveniente e simples, mas mais cedo ou mais tarde você sentirá o desejo de automatizar de alguma forma o lançamento, o teste e a coleta de resultados. Por exemplo, tenho um separado para este assunto máquina virtual no DigitalOcean, que só posso controlar usando o console.

O projeto PhantomJS permite resolver esse problema com bastante elegância. Esta não é apenas mais uma estrutura para escrever testes de unidade, mas uma versão de console completa do mecanismo WebKit. Simplificando, este aplicativo emula um navegador. Com a ajuda do PhantomJS, é possível não só automatizar a verificação da execução do teste, mas também resolver muitos problemas que mais cedo ou mais tarde surgem diante de um desenvolvedor: obtenção dos resultados da renderização da página em um arquivo (PNG, JPG), funções de monitoramento de rede (velocidade de carregamento, desempenho geral, etc.), emulação de ações do usuário e assim por diante. Eu recomendo que você reserve um tempo e leia a documentação oficial deste projeto, você definitivamente encontrará algo interessante para você;

PhantomJS pode ser compilado para diferentes plataformas (*nix, OS X, Windows). Se você desenvolve tudo no Windows, não há problemas - mescle os binários e vá em frente. Podem surgir pequenas dificuldades com a inicialização se você tiver dois adaptadores de vídeo instalados, um dos quais é NVIDIA. Neste caso, você terá que usar o hack descrito na barra lateral.


Vamos tentar nos familiarizar com o PhantomJS na prática. Para executar os testes preparados na última seção através do PhantomJS e obter os resultados da execução no console, precisamos de um script de carregamento especial - run-qunit.js. Abra o console (eu trabalho no Windows, então uso cmd) e digite o comando no formato

fantasma.exe

No meu caso, o comando de inicialização ficou assim:

E:\soft\phantomjs>phantomjs.exe E:\temp\testjsforx\qunit\run-qunit.js arquivo:///E: /temp/testjsforx/qunit/index.html

O resultado de sua execução:

Testes concluídos em 2.592 milissegundos. 9 afirmações de 9 foram aprovadas, 0 falharam.

Todos os testes passaram

Definitivamente, é necessário cobrir seu código com testes, e não importa a escala do aplicativo que você está criando. Mais uma vez, lembro a você: mesmo os menores programas se transformam em monstros desajeitados que precisam de suporte e funcionalidade adicional. Código bem testado é a chave para o sucesso e a qualidade. Sim, não é fácil começar imediatamente a escrever código adequado para testes automatizados, mas acredite, todo esse tormento será mais do que recompensado no futuro. Por hoje é tudo, boa sorte!

Problemas do PhantomJS no Windows

Aconteceu, mas testei todos os exemplos deste artigo não no Linux, mas no bom e velho Windows 7. Acontece que o PhantomJS tem pequenos problemas ao trabalhar em sistemas que usam vários adaptadores de vídeo. Além do chip de vídeo integrado, meu laptop também possui NVIDIA e, por causa disso, o PhantomJS se recusou categoricamente a responder ao comando phantom.exit(). Como resultado, após a execução do script, o processo PhantomJS não concluiu seu trabalho e continuou travando na memória. A janela do terminal também parou de responder aos comandos de desligamento (não ajudou).

Se você se deparar com um problema semelhante e planeja usar o PhantomJS no Windows, prepare-se para fazer o seguinte hack. Abra o painel Gerenciamento NVIDIA. Encontre o item “Configurações 3D” na árvore. COM lado direito A opção “Adaptador gráfico preferencial” deve aparecer. Por padrão. seu valor está definido como “Seleção automática”. Precisamos alterá-lo para “Processador NVIDIA de alto desempenho” ou “Hardware gráfico integrado”. Após esse truque simples, o PhantomJS começou a se comportar de maneira obediente.

Um dia, um amigo meu expressou sua perplexidade sobre como o JavaScript poderia ser usado para escrever produtos empresariais sérios, já que não possui um compilador. Na verdade, o papel decisivo na criação de código de alta qualidade não é desempenhado pelo fato de se ter um compilador para uma linguagem de programação, mas pelo processo técnico corretamente selecionado e bem ajustado de criação de um sistema de software.

Este processo deve incluir um conjunto de meios para controlar a qualidade e eficácia do trabalho do programador. Tais ferramentas podem ser: testes unitários e de integração, integração contínua (integração contínua, CI), coleta e análise de diversas métricas (por exemplo, métodos muito longos em nDepend), verificação de conformidade com os requisitos de JsLint, FxCop, etc.

Neste artigo, quero explicar como realizar corretamente testes automáticos de unidade e integração do seu produto em JavaScript. Na verdade, nesse aspecto, a linguagem JavaScript não é fundamentalmente diferente de Java ou C#.

Ágil, TDD e BDD

Normalmente, é recomendado criar testes automatizados de unidade e integração para funcionalidades concluídas, a fim de reduzir o risco de erros de regressão quando o código for alterado no futuro. No caso do JavaScript, tais testes podem simplificar bastante o teste da funcionalidade do sistema em diferentes navegadores, automatizando as etapas para garantir tais testes. Além disso, escrever um teste unitário ou de integração para cada bug fechado no produto pode ser uma boa ideia.

Existem também técnicas de programação que exigem que você comece a codificar a lógica escrevendo um teste de unidade: Desenvolvimento Orientado a Testes (TDD) e Desenvolvimento Orientado a Comportamento (BDD). Eles são frequentemente usados ​​no processo Agile. Vamos dar uma olhada em seus recursos.

Desenvolvimento Orientado a Testes

O desenvolvimento orientado a testes é um processo iterativo de escrita de código que repete as quatro etapas a seguir:

Passo 1. Antes de adicionar uma nova lógica, crie um teste de unidade para testar essa lógica;

Etapa 2. Faça o teste e verifique se está Não passes;

Etapa 3. Escreva o código mais simples que fará com que o teste seja bem-sucedido;

Etapa 4. Edite o código para atender aos requisitos de qualidade, remova a duplicação de código e garanta que o teste seja aprovado.

Um teste unitário é um código que testa o funcionamento de um determinado componente (módulo) em um ambiente isolado. Um teste de integração refere-se ao código que testa como vários componentes funcionam juntos. Para testar um módulo em um ambiente isolado quando este depende de outros módulos, são utilizados testes duplos.

Duplas de teste

A divisão dos objetos auxiliares utilizados em testes unitários em categorias tem origem no livro xUnit Test Patterns de Gerard Meszaros. Essas categorias são chamadas coletivamente de “duplas de teste”. Existem os seguintes tipos de substitutos:

  • Falso;
  • Fictício.

Esboço valores de saída para os quais são especificados antecipadamente. É usado para simular a interface de um componente dependente.

Mock é um objeto auxiliar comportamento que é especificado antecipadamente. É utilizado para simular a interface de um componente dependente e testar se ele está sendo utilizado corretamente em um teste.

Spy é um objeto auxiliar para inspecionar métodos chamados e parâmetros passados ​​a eles durante um teste.

Fake é um objeto auxiliar que implementa a interface de um componente dependente de forma simplificada. Por exemplo, para fins de teste de unidade, você pode ter um banco de dados na memória em vez do banco de dados relacional usado em versão de trabalho produto.

Um dummy é um objeto auxiliar cuja referência ou passagem é exigida por uma assinatura de método ou qualquer outro contrato, mas cujo valor real nunca é usado.

A diferença entre Stub e Mock é a forma como os resultados do teste são verificados. No caso do Stub, o estado do objeto é verificado ao final do teste. No caso do Mock, o teste verifica se o objeto é utilizado exatamente como descrito durante o cadastro. Detalhes podem ser encontrados na nota Mocks Aren't Stubs de Martin Fowler, e darei apenas um exemplo aqui.

Esboço Zombar
"a conexão de teste deve iniciar a pesquisa": function () ( this.client.url = "/my/url"; sinon.stub(ajax, "poll").returns()); .assert.calledWith(ajax.poll, "/meu/url" ) "a conexão de teste deve iniciar a pesquisa": function () ( this.client.url = "/my/url"; var mock = sinon.mock(ajax) mock.expects("poll") .withArgs("/my/url ").returns(); this.client.connect(); mock.verify(); )
Desenvolvimento Orientado ao Comportamento

Uma abordagem iterativa para o desenvolvimento de software por meio da implementação de requisitos funcionais é o já familiar estilo de desenvolvimento orientado a resultados e orientado a testes. No processo BDD, as três etapas a seguir são executadas sequencialmente:

Passo 1. Determinação de requisitos funcionais do módulo implementado em forma de testes;

Etapa 2. Codificação de módulos;

Etapa 3. Verificar se todos os desejos do cliente ou analista de negócios () são atendidos através da verificação dos resultados da execução de testes.

Ao escrever testes no estilo BDD, é muito conveniente usar objetos Mock devido ao fato de que eles refletem perfeitamente os requisitos funcionais de um componente. Assim, os testes no processo BDD podem servir como uma representação formalizada da tarefa (história do usuário) em termos Scrum, o que economiza tempo na redação de especificações técnicas e documentação do produto acabado.

O que deveria ser uma estrutura de teste de unidade JavaScript?

Uma ferramenta completa de teste de unidade e integração JavaScript deve consistir nos seguintes componentes:

  • Biblioteca de asserções (conjunto de métodos para verificar o estado de um componente ao final de cada teste);
  • Biblioteca Mock (uma ferramenta para gerar objetos Mock e outras “duplicatas”);
  • Test runner (uma ferramenta para execução automática de testes com suporte para a maioria dos navegadores, incluindo navegadores iOS e Android);
  • Bloco de conexão para sistemas populares de integração contínua.
Estratégias para teste de unidade de código JavaScript

Hoje, existem três estratégias para testar unidades de código JavaScript (para obter mais detalhes, consulte o terceiro capítulo do livro Test-Driven JavaScript Development, de Christian Johansen):

  • Testes no navegador;
  • Teste sem cabeça;
  • Testando ao longo do caminho JsTestDriver.

O teste no navegador envolve a execução de todos os testes de unidade e integração a partir de uma página HTML, que o desenvolvedor abre nos navegadores desejados de forma independente. Essa abordagem é simples e intuitiva. Porém, sua desvantagem é que não prevê a possibilidade de inclusão de tais testes na Integração Contínua. Além disso, executar manualmente uma página HTML em dez ou mais navegadores e pressionar “F5” constantemente pode ser entediante para um desenvolvedor.

O teste headless significa que todo o código JavaScript é testado em um emulador, que pode ser escrito em Java, Ruby, JavaScript, C++, etc. O emulador mais famoso hoje é o PhantomJS, que é um motor WebKit que roda a partir de linha de comando. Dentre as vantagens do emulador, pode-se destacar que ele pode ser facilmente utilizado em Integração Contínua, e também que permite automatizar o lançamento de todos os testes a partir da linha de comando. No entanto, esta abordagem tem uma desvantagem significativa - o código não é testado para navegadores reais, portanto, há o risco de erros de navegador ausentes que não podem ser reproduzidos no emulador. Antes do advento do JsTestDriver, muitas vezes era possível ver que os testes In-Browser eram combinados com os testes Headless, uma vez que eles se complementam perfeitamente.

Javascript é uma linguagem muito popular para desenvolvimento web do lado do cliente. JavaScript é uma linguagem dinâmica e é baseada em prototipagem. Apesar do nome, ele não pertence à linguagem Java, embora use uma sintaxe semelhante à do C e convenções de nomenclatura semelhantes.

Recentemente, o Javascript começou a ganhar cada vez mais popularidade. Está subjacente a isto tecnologia moderna como AJAX. O conhecimento de javascript também pode ser útil para programação do lado do servidor para configuração flexível do sistema.

Público-alvo do teste JavaScript

Este teste é recomendado para especialistas que desejam testar seus conhecimentos básicos de JavaScript. O teste também pode ser usado para consolidar o conhecimento dos desenvolvedores imediatamente após a familiarização com a linguagem JavaScript.

Em maior instituições educacionais Esta prova será do interesse de estudantes de especialidades técnicas nas áreas de “Engenharia de Software” ou “ Programas Internet".

pré-requisitos de teste javascript

Antes de fazer este teste, você precisa ter pelo menos um conhecimento básico da linguagem Javascript.

O teste é direcionado principalmente a especialistas que aprenderam JavaScript recentemente. Portanto, este teste não cobre tópicos complexos como OOP em JavaScript ou APIs JavaScript especializadas.

Estrutura de teste JavaScript

As questões do teste cobrem os seguintes tópicos:

  • construções básicas da linguagem JavaScript (declaração de variáveis, funções, etc.);
  • operadores (ramificação, loops);
  • trabalhando com strings em JavaScript;
  • trabalhando com arrays em JavaScript;
  • Pacote "JavaScript - HTML" (tags usadas para integração)
  • outros conceitos que não estão incluídos nos tópicos descritos acima.
Desenvolvimento adicional do teste JavaScript

A próxima etapa no desenvolvimento do teste Javascript será criar perguntas com base nos tópicos de teste existentes. Expandir a lista de perguntas aumentará o interesse em fazer o teste novamente, tanto para teste de conhecimento quanto para fins de aprendizagem.

Usando o exemplo de um aplicativo de calculadora simples em Node.js. Testaremos usando o framework Mocha.

O que nosso aplicativo deve ser capaz de fazer:

  • Adicionar, subtrair, dividir e multiplicar quaisquer dois números;
  • Mostrar um aviso e sair se algo diferente de um número for digitado;
  • Também deve haver uma interface de linha de comando para o usuário final usar o aplicativo.

O que precisamos:

  • Node.js e npm;
  • Conhecimento de JavaScript: sintaxe e estrutura de código, tipos de dados, operações matemáticas e expressões condicionais.

Agora que você definiu seus objetivos, pode começar a configurar seu ambiente para teste e desenvolvimento.

Configurando o ambiente

Como estamos usando Node.js, precisamos criar um ambiente local para os arquivos e dependências.

Criar nova pasta cálculo. Na linha de comando, vá para este diretório e crie um novo projeto com o comando npm init, que irá criar novo arquivo pacote.json para o nosso programa.

Você será solicitado a inserir o nome do pacote, versão, descrição e outras informações sobre o pacote. Você pode inserir um nome calc.js continue pressionando Digitar para atribuir valores padrão. Quando você chegar ao comando test, digite mocha - esta é a estrutura de teste que usaremos:

comando de teste: mocha

Após inserir todas as informações, o script criará um arquivo pacote.json, que se parece com isto:

( "nome": "calc.js", "versão": "1.0.0", "descrição": "Calculadora simples em Node.js", "main": "index.js", "scripts": ( " teste": "mocha" ), "autor": "", "licença": "ISC" )

A última etapa nesta fase é instalar o Mocha. Digite o seguinte comando para instalar:

Instalação Npm --save-dev mocha

Depois de usar este comando a pasta aparecerá node_modules, arquivo pacote-lock.json, e no arquivo pacote.json aparecerão as seguintes linhas:

"devDependencies": ("mocha": "^4.0.1")

Crie um arquivo teste.js. Usaremos o módulo integrado ao Node.js afirmar para verificar se verdadeiro e verdadeiro são verdadeiros. Como isso é verdade, o teste deve passar:

Const afirmar = exigir("afirmar"); it("deve retornar verdadeiro", () => ( assert.equal(true, true); ));

Agora execute o teste na linha de comando:

$ npm test > mocha ✓ deve retornar true 1 passagem (8ms)

O teste ocorreu conforme o esperado, então concluímos a configuração do ambiente. Remover de teste.js tudo exceto a linha const assert = require("assert"); .

Usaremos o arquivo teste.js durante todo o processo de criação do aplicativo. Crie mais dois arquivos: operações.js para funções aritméticas e de validação e calc.js para o próprio aplicativo. Usamos tantos arquivos para evitar que se tornem muito longos e complexos. Aqui está nossa lista atual de arquivos:

  • calc.js;
  • node_modules;
  • operações.js;
  • pacote-lock.json;
  • pacote.json;
  • teste.js;

Vamos adicionar o primeiro teste real para nossa aplicação.

Adicionando operações matemáticas

Em primeiro lugar, a nossa aplicação deve ser capaz de somar, subtrair, dividir e multiplicar quaisquer dois números. Isso significa que para cada uma dessas operações devemos criar uma função separada.

Vamos começar com a adição. Escreveremos um teste no qual a soma esperada de dois números será definitivamente obtida. No código abaixo verificamos se a soma de 1 e 3 é igual usando a função add() 4:

Const afirmar = exigir("afirmar"); it("encontra corretamente a soma de 1 e 3", () => ( assert.equal(add(1, 3), 4); ));

Depois de executar o teste usando o comando npm test, vemos o seguinte:

> mocha 0 passando (9ms) 1 falhando 1) encontra corretamente a soma de 1 e 3: ReferenceError: add não está definido em Context.it (test.js:5:16) npm ERR! O teste falhou. Veja acima para mais detalhes.

O teste falhou com a mensagem ReferenceError: add is not Define . Estamos testando a função add(), que ainda não existe, então esse resultado é bastante esperado.

Vamos criar a função add() no arquivo operações.js:

Const adição = (x, y) => (+x) + (+y);

Esta função recebe dois argumentos x e y e retorna sua soma. Você deve ter notado que escrevemos (+x) + (+y) em vez de x + y. Usamos o operador unário para converter o argumento em um número caso a entrada seja uma string.

Nota Isso usa a função de seta adicionada no ES6 e retorno implícito.

Como estamos usando Node.js e dividindo o código em vários arquivos, precisamos usar module.exports para exportar o código:

Const adição = (x, y) => (+x) + (+y); module.exports = (adicionar)

No início do arquivo teste.js importamos o código de operações.js usando require() . Como estamos usando a função por meio da variável operações, precisamos alterar add() para operações.add() :

Operações const = require("./operações.js"); const assert = require("assert"); it("encontra corretamente a soma de 1 e 3", () => ( assert.equal(operações.add(1, 3), 4); ));

Vamos fazer o teste:

$ npm test > mocha ✓ encontra corretamente a soma de 1 e 3 1 passando (8ms)

Agora temos uma função funcionando e os testes estão passando. Como as outras funções de operação funcionam de forma semelhante, adicionar testes para subtract() , multiplicar() e dividir() é fácil:

It("encontra corretamente a soma de 1 e 3", () => ( assert.equal(operações.add(1, 3), 4); )); it("encontra corretamente a soma de -1 e -1", () => ( assert.equal(operações.add(-1, -1), -2); )); it("encontra corretamente a diferença entre 33 e 3", () => ( assert.equal(operações.subtract(33, 3), 30); )); it("encontra corretamente o produto de 12 e 12", () => ( assert.equal(operações.multiply(12, 12), 144); )); it("encontra corretamente o quociente de 10 e 2", () => ( assert.equal(operações.divide(10, 2), 5); ));

Agora vamos criar e exportar todas as funções para teste.js:

Const adição = (x, y) => (+x) + (+y); const subtrair = (x, y) => (+x) - (+y); const multiplicar = (x, y) => (+x) * (+y); const divisão = (x, y) => (+x) / (+y); module.exports = (adicionar, subtrair, multiplicar, dividir,)

E vamos fazer novos testes:

$ npm test > mocha ✓ encontra corretamente a soma de 1 e 3 ✓ encontra corretamente a soma de -1 e -1 ✓ encontra corretamente a diferença de 33 e 3 ✓ encontra corretamente o produto de 12 e 12 ✓ encontra corretamente o quociente de 10 e 2 5 passes (8ms)

Todos os testes foram aprovados com sucesso, agora podemos ter certeza de que as principais funções de nossa aplicação funcionarão corretamente. Agora você pode fazer alguma validação adicional.

Adicionando validação

Sobre no momento quando o usuário digita um número e seleciona a operação desejada, tudo funciona bem. No entanto, o que acontece se você tentar encontrar a soma de um número e uma string? A aplicação tentará realizar a operação, mas por esperar um número, retornará NaN.

Em vez de retornar algum valor estranho, é hora de executar a segunda tarefa - fazer o aplicativo mostrar um aviso e sair se o argumento inserido não for um número.

Primeiro você precisa escrever uma função que irá verificar se a entrada é um número ou não. A aplicação deve funcionar apenas com números, por isso iremos tratar de três situações:

  • Ambas as entradas são números.
  • Uma entrada é um número e a outra é uma string.
  • Ambas as entradas são strings.
  • it("relata um erro ao usar uma string em vez de um número", () => ( assert.equal(operações.validateNumbers("sammy", 5), false); )); it("relata um erro ao usar duas strings em vez de números", () => ( assert.equal(operações.validateNumbers("sammy", "sammy"), false); )); it("sucesso ao usar dois números", () => ( assert.equal(operações.validateNumbers(5, 5), true); ));

    A função validNumbers() verificará ambos os parâmetros. A função isNaN() verifica se o parâmetro é um número e, caso contrário, retorna false . Caso contrário, retorna verdadeiro, indicando validação bem-sucedida.

    Const validNumbers = (x, y) => ( if (isNaN(x) && isNaN(y)) ( return false; ) return true; )

    Não se esqueça de adicionar activateNumbers a module.exports no final do arquivo. Agora você pode executar novos testes:

    $ npm test 1) relata um erro ao usar uma string em vez de um número ✓ relata um erro ao usar duas strings em vez de um número ✓ sucesso ao usar dois números 7 passando (12ms) 1 falhando 1) relata um erro ao usar uma string em vez de um número: AssertionError: true == false + esperado - actual -true +false

    Dois testes foram aprovados, mas um falhou. O teste para dois números foi aprovado com êxito, assim como o teste para duas strings. O mesmo não pode ser dito sobre a verificação de entrada de strings e números.

    Se você olhar nossa função novamente, notará que ambos os parâmetros devem ser NaN para que a função retorne false . Se quisermos obter o mesmo efeito quando pelo menos um dos parâmetros for NaN , precisamos substituir && por || :

    Const ValidateNumbers = (x, y) => ( if (isNaN(x) || isNaN(y)) ( return false; ) return true; )

    Se após essas alterações você executar o teste npm novamente, todos os testes serão aprovados com sucesso:

    ✓ relata um erro ao usar uma string em vez de um número ✓ relata um erro ao usar duas strings em vez de um número ✓ sucesso ao usar dois números 8 passagem (9ms)

    Testamos todas as funcionalidades do nosso aplicativo. As funções executam operações matemáticas e validam a entrada com êxito. A etapa final é a criação da interface do usuário.

    Criando uma interface

    Já temos as funções necessárias, mas o usuário ainda não pode utilizá-las. Portanto, precisamos de uma interface. Para nossa aplicação criaremos uma interface de linha de comando.

    Atualmente o arquivo calc.js deve estar vazio. É aqui que nosso aplicativo será armazenado. Primeiro você precisa importar funções de operações.js:

    Operações const = require("./operações.js");

    A própria interface usará o módulo Readline CLI integrado ao Node.js:

    Const readline = require("readline");

    Depois de importar tudo que você precisa, você pode começar a criar seu aplicativo. Para criar a interface usaremos readline, acessível através da variável rl:

    Const rl = readline.createInterface(( entrada: process.stdin, saída: process.stdout ));

    A primeira coisa que o usuário deve ver após iniciar o programa é uma mensagem de boas-vindas e instruções de uso. Para fazer isso usaremos console.log() :

    Console.log(` Calc.js Você abriu uma calculadora em Node.js! Versão: 1.0.0. Uso: O usuário deve inserir dois números e depois escolher o que fazer com eles. `);

    Antes de entrarmos nas funções da calculadora, vamos verificar se console.log() está funcionando conforme o esperado. Faremos o programa imprimir uma mensagem e sair. Para fazer isso, adicione uma chamada ao método rl.close() no final.

    Para executar o aplicativo, insira o nó e o nome do arquivo:

    $ node calc.js Calc.js Você abriu uma calculadora Node.js! Versão: 1.0.0. Utilização: O usuário deve inserir dois números e depois escolher o que fazer com eles.

    O programa exibe uma mensagem de boas-vindas e sai. Agora precisamos adicionar a entrada do usuário. O usuário é obrigado a fazer o seguinte: selecionar dois números e uma operação. Cada entrada será solicitada pelo método rl.question():

    Rl.question("Digite o primeiro número: ", (x) => ( rl.question("Digite o segundo número: ", (y) => ( rl.question(` Selecione uma das seguintes operações: Adição ( +) Subtração (-) Multiplicação (*) Divisão (/) Sua escolha: `, (choice) => ( // o código rl.close(); ));

    A variável x recebe o primeiro número, y o segundo, e escolhe a operação selecionada. Agora nosso programa pede entrada, mas não faz nada com os dados que recebe.

    Após a terceira entrada, você precisa verificar se apenas números foram inseridos. Para fazer isso, usaremos a função validNumbers(). Utilizando o operador NOT, verificaremos se os números foram inseridos e, caso contrário, encerraremos o programa:

    If (!operações.validateNumbers(x, y)) ( console.log("Apenas números podem ser inseridos! Reinicie o programa."); )

    Se tudo for inserido corretamente, agora você precisa executar o método correspondente à operação criada anteriormente. Para processar quatro opções possíveis escolha, usaremos uma expressão switch e imprimiremos o resultado da operação. Caso uma operação inexistente tenha sido selecionada, um bloco padrão será executado, informando ao usuário para tentar novamente:

    If (!operações.validateNumbers(x, y)) ( console.log("Apenas números podem ser inseridos! Reinicie o programa."); ) else ( switch (choice) ( case "1": console.log(` Soma $(x) e $(y) é igual a $(operações.add(x, y)).`); break; case "2": console.log(`A diferença de $(x) e $( y) é $(operações.subtract(x, y)).`); break; case "3": console.log(`O produto de $(x) e $(y) é $(operações.multiply(x). , y)).`) ; break; case "4": console.log(`O quociente de $(x) e $(y) é $(operações.divide(x, y)).`); reinicie o programa e selecione um número de 1 a 4."); break; ) )

    Nota As funções console.log() aqui usam strings de modelo que permitem expressões.

    /** * Uma calculadora simples em Node.js que usa o aplicativo de calculadora que usa * a interface de linha de comando Readline integrada.

    */ operações const = require("./operações.js"); const readline = require("readline"); // Use readline para criar a interface const rl = readline.createInterface(( input: process.stdin, output: process.stdout )); console.log(` Calc.js Você abriu uma calculadora em Node.js! Versão: 1.0.0. Uso: O usuário deve inserir dois números e depois escolher o que fazer com eles. `); rl.question("Digite o primeiro número: ", (x) => ( rl.question("Digite o segundo número: ", (y) => ( rl.question(` Selecione uma das seguintes operações: Adição ( +) Subtração (-) Multiplicação (*) Divisão (/) Sua escolha: `, (choice) => ( if (!operações.validateNumbers(x, y)) ( console.log("Apenas números podem ser inseridos! Por favor reinicie o programa. "); ) else ( switch (escolha) ( case "1": console.log(`A soma de $(x) e $(y) é $(operações.add(x, y)). `); break; case "2": console.log(`A diferença entre $(x) e $(y) é $(operações.subtract(x, y)).`); console.log(`O produto de $( x) e $(y) é igual a $(operações.multiply(x, y)).`); $(x) e $(y) são iguais a $(operações. divide(x, y)).`); break; default: console.log("Por favor, reinicie o programa e selecione um número de 1 a 4." ) quebrar; ; ));

    Agora nosso aplicativo está pronto. Vamos verificar seu funcionamento uma última vez. Digite 999 e 1 e selecione a operação de subtração:

    O programa concluiu seu trabalho com sucesso, produzindo o resultado correto. Parabéns, você escreveu uma calculadora simples usando Node.js e aprendeu o básico do desenvolvimento de TDD.

    Análise