Vous êtes sur la page 1sur 95

Nodebots - Javascript e robótica no mundo

real
Wilson Mendes
Esse livro está à venda em http://leanpub.com/nodebots-javascript-e-robotica-no-mundo-real

Essa versão foi publicada em 2017-10-17

Esse é um livro Leanpub. A Leanpub dá poderes aos autores e editores a partir do processo de
Publicação Lean. Publicação Lean é a ação de publicar um ebook em desenvolvimento com
ferramentas leves e muitas iterações para conseguir feedbacks dos leitores, pivotar até que você
tenha o livro ideal e então conseguir tração.

© 2016 - 2017 Wilson Mendes


Conteúdo

Sobre o autor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

Agradecimentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

Nodebots e microcontroladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
O que são nodebots? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Microcontroladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
NodeJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Gerenciando dependências com o NPM . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Firmata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Johnny Five . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

Primeiro projeto: Build Checker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21


O que é um build pipeline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Criando um Build Checker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Anatomia de um verificador de build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Material necessário . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Criando a requisição das informações de build no CI/CD . . . . . . . . . . . . . . . . . . 25
Ajustando a arquitetura de nossa aplicação . . . . . . . . . . . . . . . . . . . . . . . . . 30
Criando testes unitários para o build checker . . . . . . . . . . . . . . . . . . . . . . . . 32

Segundo projeto: Alarme de incêndio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42


Anatomia de um alarme de incêndio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Material necessário . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Controlando o sensor de chamas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Evoluindo o nosso código inicial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Integrando com o Piezo para aviso sonoro . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Enviando SMS para o seu celular usando o Twilio . . . . . . . . . . . . . . . . . . . . . . 51

Dando suporte ao seu código em vários sistemas operacionais . . . . . . . . . . . . . . . 66


Adicionando servidores de integração contínua ao seu projeto . . . . . . . . . . . . . . . 66
CONTEÚDO

Code coverage para o seu código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

Apêndice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Protoboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Piezo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Resistores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
LED (Light-emitting diode) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Sensores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Condutor de Proteção (Fio Terra ou Ground) . . . . . . . . . . . . . . . . . . . . . . . . . 89
Fios de condução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Botão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

Próximos passos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Sobre o autor
GDE (Google Developer Expert) AngularJS, GDG Salvador organizer, apaixonado por tecnologia e
ativo em comunidades com foco em desenvolvimento web, incluindo AngularJS, JavaScript, HTML5,
CSS3, workflow, web performance, segurança e Internet of things. Participa na organização de
eventos, palestra em conferências no Brasil e em outros países e contribui para projetos opensource
como Oh-My-Zsh1 , AngularJS2 , Webpack3 , dentre outros.
1
https://github.com/robbyrussell/oh-my-zsh
2
https://github.com/angular
3
https://github.com/webpack
Introdução
Esta parte do livro é direcionada a todos que desejam dar os primeiros passos sobre Nodebots ou que
tem interesse em aprofundar-se em alguns conceitos que são pouco demonstrados sobre o assunto.
Serão mostrados conteúdos com sensores simples e de baixo custo, porém relacionando os sensores
com integrações reais de uma aplicação nodebots, como integração entre API’s externas a partir de
eventos leitura de alguns dados de sensores, dentre outros.
Além disto o livro aborda tópicos importantes nos quesitos de testes, mostrando boas práticas para
a cobertura de testes e como testar com uma boa qualidade e performático, com integrações em
serviços externos para automatização de tarefas de validação de qualidade de código e build.
Outro ponto abordado é o quesito de evolução da aplicação com foco na arquitetura. Como utilizar
padrões de arquitetura de software em qualquer tipo de projeto e como tirar proveito de cada um
deles aplicando no seu sistema.
Agradecimentos
Gostaria de agradecer inicialmente a minha mãe (que fez o papel de mãe e pai por muitas vezes),
essa pessoa maravilhosa que me ensinou muitos dos valores e que levo comigo até hoje, sendo o
meu exemplo até hoje. Marco Marocci e Robson Mário, que são como pais para mim e ao meu irmão
Leonardo Araújo que sempre esteve comigo quando eu precisei e que me deu também mais 2 irmãos,
Ramon Pombo e Matheus Pombo.
Gostaria de agradecer a minha mulher, Nilana Rocha, por todo apoio dado. Ela foi um dos fatores
cruciais e com certeza sem ela este livro não seria uma realidade. Muito obrigado por tudo.
Um grande momento para lembrar de todos os meus amigos do Grupo LinguÁgil e #horaExtraSSA
de Salvador-BA. Vocês foram pessoas que me motivaram a ser o profissional que sou atualmente,
que me motivaram a buscar e compartilhar conhecimento. Obrigado a todos vocês. Ao pessoal de
Recife que são pessoas que tenho muito carinho e me apresentaram queijo coalho, macaxeira (ou
aipim?) e muito carinho.
Por último, gostaria de agradecer Nelson Glauber4 e Fagner Brack5 pelo suporte na revisão do
conteúdo deste livro e a todos que me ajudaram direta ou indiretamente na composição deste livro,
como Rafael Gomes6 , Juliano Bersano7 , Sarah Atkinson8 e vários outros que me deram suporte neste
processo e sugeriram melhorias nesta etapa.
Muito obrigado a todos.
Wilson Mendes
4
https://twitter.com/nglauber
5
https://twitter.com/fagnerbrack
6
https://twitter.com/gomex
7
https://twitter.com/julianobersano
8
https://twitter.com/SarahvAko
Nodebots e microcontroladores
O que são nodebots?
NodeBots é um termo utilizado para definir o conceito de controle sobre open hardware, um
hardware eletrônico projetado e oferecido da mesma maneira e com as mesmas licenças que um
software de código livre, sensores e outros componentes eletrônicos utilizando NodeJS. E você pode
utilizar vários elementos: desde sensores, servo motores9 , rodas, detectores de movimento, câmeras,
displays de LED, reprodutores de áudio e muito mais.
Em alguns momentos o conceito de Nodebots está diretamente conectado ao conceito de IoT -
Internet das Coisas. Do inglês Internet of Things é uma revolução tecnológica a fim de conectar
aparelhos eletrônicos do nosso cotidiano, como aparelhos eletrodomésticos e até mesmo máquinas
industriais e aparelhos com acesso à internet e outras inovações técnicas em campos importantes
como o da automação domiciliar a partir de sensores.
Toda a ideia de NodeBots evoluiu de acordo com as capacidades crescentes no NodeJS e o esforço de
alguns desenvolvedores como Nikolai Onken, Jörn Zaefferer, Chris Williams, Julian Gautier e Rick
Waldron que trabalharam para desenvolver os vários módulos que usamos em aplicações NodeBots
hoje em dia. O módulo node-serialport10 , criado por Chris Williams, foi o pontapé inicial pois permite
o acesso a dispositivos utilizando processos de leitura e escrita a portas seriais em baixo nível.
Julian Gautier, em seguida, implementou o Firmata11 , um protocolo usado para acesso a microcon-
troladores, como Arduinos, via código usando JavaScript para a comunicação entre componentes
físicos.
Rick Waldron foi um pouco mais além. Usando a biblioteca Firmata como base, ele criou um
framework para auxiliar na construção de Nodebots e Internet das coisas chamado Johnny-Five.
O framework Johnny-Five torna o controle de vários componentes, desde LEDs até vários outros
tipos de sensores de uma maneira simples e prática. Isto é o que muitos NodeBots agora usam para
atingir alguns feitos impressionantes!

Microcontroladores
Quando falamos de nodebots, estamos indiretamente mencionando microcontroladores, que é um
computador menor e mais simples. Ele possui uma placa programável simples de circuito físico
(citaremos como pinos, entradas, etc) que pode detectar várias entradas e saídas.
9
https://pt.wikipedia.org/wiki/Servomotor
10
https://www.npmjs.com/package/serialport
11
https://www.npmjs.com/package/firmata
Nodebots e microcontroladores 5

Um Arduino é um dos vários tipos de microcontroladores, sendo um dos mais comuns para
experimentos e validações entre software e hardware, mas existem outros tipos que pode ser
alimentado por NodeJS, como por exemplo:

• Raspberry Pi12 ;
• Tessel13 ;
• Espruino14 ;
• BeagleBone15 ;

Neste livro utilizarei o Arduino UNO16 nos exemplos, mas sinta-se livre para utilizar o microcontro-
lador de sua escolha.

NodeJS
NodeJS é um runtime de execução JavaScript construído com base na engine Javascript V8 do
Chrome, possibilitando a utilização do Javascript em outros ambientes além da web e com um
aspecto importante que é a utilização de um modelo não-bloqueante de entrada e saída de dados
orientado a eventos. Possui o objetivo de ajudar programadores na criação de aplicações de alta
escalabilidade como servidores web com conexões simultâneas, scripts assíncronos e até mesmo a
integração com componentes eletrônicos que é o nosso caso.
Foi criado por Ryan Dahl em 2009, e seu desenvolvimento é mantido pela comunidade e pela Node
Foundation, da qual empresas como IBM, Google, Red Hat, Joyent, dentre outras.

Instalando no Windows
Instalar o NodeJS no Windows é bem simples. Uma das maneiras é visitar o website oficial do
projeto17 e baixar o instalador no formato, clicar nas opções de instalação e finalizar a instalação.
Quando finalizar abra o seu prompt de comando acessando pelo prompt de comando do Windows
a partir do comando “Executar > cmd” e, após iniciar o programa, digite o seguinte comando:

1 $ node -v

Ele deve exibir no prompt a versão atual do NodeJS no seu terminal. Com isso finalizamos a
instalação no ambiente Windows.
12
https://www.raspberrypi.org/
13
https://tessel.io/
14
http://www.espruino.com/
15
http://beagleboard.org/bone
16
https://www.arduino.cc/en/Main/ArduinoBoardUno
17
https://nodejs.org/en/download/
Nodebots e microcontroladores 6

Instalando no Linux e Mac OS X


Para os sistemas Linux e Mac OS X você pode utilizar vários formatos como efetuar o download do
node no site (como fizemos para a instalação no Windows), via gerenciador de pacotes do próprio
sistema operacional, mas uma forma de unificar o formato de instalação para as plataformas é utilizar
o NVM - Node Version Manager18 que é um gerenciador de versões do NodeJS baseado em bash
script.
Sua instalação é bem simples. Você pode instalar localmente via Curl ou Wget, respectivamente:
Curl:

1 $ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.6/install.sh |\


2 bash

Wget:

1 $ wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.6/install.sh \


2 | bash

Pacotes como CURL e WGET podem não estar instalados em seu sistema operacional
por padrão. Caso precise, verifique a melhor forma de instalação para o seu sistema
operacional ou acesse o repositório do NVM no Github para verificar os passos de
instalação ou possíveis soluções de problema.

Logo a seguir, você deve abrir o seu arquivo que guarda a configuração padrão de seu terminal,
que pode estar localizado no ∼/.bash_profile, ∼/.zshrc, ∼/.profile ou ∼/.bashrc, e adicionar
estas linhas no final deste arquivo de configuração para que carregue o NVM no momento em que
você acesse a linha de comando.

1 export NVM_DIR="$HOME/.nvm"
2 [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm

Com isso, assim que você recarregar o seu terminal o NVM estará disponível. Agora basta instalar
a versão do NodeJS de sua preferência. Neste livro utilizaremos a versão 5.3.0.

1 $ nvm install 5.3.0


2 $ nvm use 5.3.0
3 $ nvm alias default 5.3.0

Após estes comandos o NVM fará o download da versão específica do NodeJS, deixando-a acessível
via terminal. Para verificar se o comando foi concluído com sucesso, digite o comando:
18
https://github.com/creationix/nvm
Nodebots e microcontroladores 7

1 $ node -v

O resultado deve ser v5.3.0. Se este foi o retorno do seu comando, está tudo pronto para os nossos
próximos passos. Caso tenha algum problema verifique se o código de carregamento do NVM foi
inserido no arquivo de configuração do seu terminal e inicie outra instância do seu terminal.
Foi criado um arquivo com os comandos contidos neste tópico para a instalação do NVM e o Node
com a versão utilizada neste livro. Caso queira utilizá-lo, por favor faça o download do arquivo
nvm-install.sh19 .

Gerenciando dependências com o NPM

Iniciando o seu projeto e conhecendo o arquivo package.json


Como primeiro passo vamos criar a pasta “hello-world” e adicionaremos informações iniciais para
nosso projeto. Para isso usaremos o comando npm init20 .

Comando npm init

Para continuar será necessário responder a algumas perguntas básicas sobre o projeto, tais como: -
Nome do pacote; - Versão do projeto; - Descrição sobre o projeto; - Nome do arquivo principal do
projeto. Este será produzido no final do seu projeto, depois de todos os tratamentos de minificação,
obfuscação e outros procedimentos de otimização do seu código javascript;
19
https://gist.github.com/willmendesneto/4c951413bacbb8850837a53bcdada30d
20
https://docs.npmjs.com/cli/init
Nodebots e microcontroladores 8

Pode ficar tranquilo que nenhuma delas é obrigatória. Caso não saiba ou não queira responder agora,
basta clicar na tecla “Enter” até o final. Em seguida, ele vai criar um arquivo package.json com estas
informações do seu repositório.

output do comando

Adicionando pacotes
Agora que já temos o nosso package.json21 com todas as configurações básicas do nosso repositório,
vamos instalar o nosso primeiro pacote para integrarmos com o nosso projeto!
O NPM funciona como o gerenciador de pacotes oficial para aplicações NodeJS, sendo o maior
ecossistema de bibliotecas e módulos open source do mundo.
Com ele você pode adicionar pacotes em sua aplicação a partir do comando npm install22 informando
o nome do pacote publicado no site oficial do npm e o formato que queremos salvar o pacote na
aplicação. Temos algumas opções de adição de pacotes que são:

• localmente como uma dependência de desenvolvimento: o pacote será instalado localmente e


acessível na pasta node_modules e as informações do pacote serão salvas na chave devDepen-
dencies do seu arquivo JSON. Para usar esta opção adicione a flag --save-dev;
21
https://docs.npmjs.com/files/package.json
22
https://docs.npmjs.com/cli/install
Nodebots e microcontroladores 9

• localmente como uma dependência do seu projeto: o pacote será instalado localmente e aces-
sível na pasta node_modules e as informações do pacote serão salvas na chave dependencies
do seu arquivo JSON. Para usar esta opção adicione a flag --save;
• globalmente: o pacote será instalado com escopo global e acessível em qualquer outro projeto.
Para usar esta opção adicione a flag --global;

Pacotes salvos como dependência de desenvolvimento e globalmente não serão utiliza-


dos quando você publicar o seu projeto, então utilize estas opções com cuidado.

Para o nosso projeto inicial vamos instalar o framework Johnny-Five como uma das dependências
de desenvolvimento, rodando o comando:

1 $ npm install --save johnny-five

Instalando o pacote Johnny-Five


Nodebots e microcontroladores 10

Você pode perceber que agora temos alguns arquivos novos na pasta do nosso projeto.
Primeiramente, foi criada a pasta node_modules e dentro dela temos o nosso pacote instalado com
sucesso.

listando a pasta node_modules

Outra novidade foi a adição das informações do nosso pacote no bloco de conteúdo dependencies
no nosso arquivo package.json.

1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "test": "echo \"Error: no test specified\" && exit 1"
8 },
9 "author": "",
10 "license": "ISC",
11 "dependencies": {
12 "johnny-five": "^0.10.0"
13 }
14 }

O npm possui vários outros comandos padrão que podemos utilizar em nossa aplicação.
Caso queira saber mais sobre estes comandos, acesse a página sobre estes comandos na
documentação oficial do NPM23 .
23
https://docs.npmjs.com/misc/scripts
Nodebots e microcontroladores 11

Adicionando comandos NPM


O NPM é uma ferramenta muito interessante e bastante flexível, com a possibilidade de criar de
comandos específicos executados a partir do npm run seu-comando.
No nosso exemplo vamos criar um comando de exemplo para rodarmos o nosso código. Vamos abrir
o nosso package.json no nosso editor/IDE de preferência e adicionarmos o seguinte código:

1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "dev": "echo \"This is our 'dev' NPM task\"",
8 "test": "echo \"Error: no test specified\" && exit 1"
9 },
10 "author": "",
11 "license": "ISC",
12 "dependencies": {
13 "johnny-five": "^0.10.0"
14 }
15 }

Notem que na linha 7, em negrito, adicionamos o nosso novo comando NPM através do campo
“scripts” do JSON. Reparem que já tínhamos o comando “test” que é um comando padrão do npm
e está na mesma área do nosso novo comando, pois esta é a área padrão para adicionarmos os
comandos a serem executados pelo NPM.
Para rodarmos o comando, basta acessarmos o nosso terminal ou prompt de comando e digitarmos
npm run dev. O resultado retornará da seguinte forma:
Nodebots e microcontroladores 12

Arduino

Arduino…ardu-o-que?
Arduino é uma plataforma open-source baseada em um hardware de fácil utilização e integração
com sensores a partir do software. Por tratar-se de uma plataforma totalmente maleável e aberta
qualquer um pode utilizá-lo em projetos dos mais diversos como simples verificações de dados
recebidos por sensores de luz, temperatura, umidade ou até mesmo automação domiciliar.
Dentre as suas vantagens encontramos:

• Custo: o valor de um Arduino é muito baixo. Atualmente o custo de um Arduino UNO24 é


algo em torno de R$50,00 e o Arduino Nano25 custa entre R$ 15,00 a R$ 20,00, podendo ser
mais ou menos de acordo com o modelo de sua escolha;
• Cross-platform: Arduino é compatível com todos os sistemas operacionais e plataformas;
• Simples: Não exige de quem vai manipulá-lo um vasto conhecimento em eletrônica. Basta ter
uma noção básica de desenvolvimento e você já pode fazer coisas bem bacanas;

Sobre Open source hardware


Open source hardware é um hardware eletrônico com a mesma cultura de um software de código
livre. Este termo utilizado pela primeira vez com o intuito de refletir a ideia de informação aberta
e pública quanto ao hardware, como diagramas, estruturas de produtos e dados de layout de uma
placa de circuito impresso.
Com o crescimento dos dispositivos lógicos programáveis, o compartilhamento dos esquemas lógicos
de forma aberta também se espalhou. Neste caso as especificações do hardware estão disponíveis
para todos. Ou seja, você pode criar ou evoluir o seu hardware a partir daquele conteúdo sem nenhum
problema.

Instalando Arduino IDE


A instalação do Arduino IDE é bastante simples. Basta acessarmos o site oficial do projeto26 e na
página principal podemos encontrar todas as opções de download por sistema operacional. Verifique
qual o seu sistema operacional e faça o download do instalador.
24
https://www.arduino.cc/en/Main/ArduinoBoardUno
25
https://www.arduino.cc/en/Main/ArduinoBoardNano
26
https://www.arduino.cc/en/Main/Software
Nodebots e microcontroladores 13

Site oficial do projeto Arduino

Existe uma página na Wiki do projeto Arduino com soluções para os problemas mais comuns27 , caso
vocês tenham algum tipo de inconveniente com a instalação e primeiro setup do Arduino IDE.

Setup inicial do Arduino

É possível codificar utilizando o seu editor ou IDE preferido e iniciar esta etapa
utilizando o pacote interchange28 . O intuito do conteúdo a seguir é facilitar o setup
do arduino para desenvolvedores que estão tendo o primeiro contato com a plataforma
Arduino.

Feita a instalação do Arduino IDE, vamos agora acessar o programa e verificar o seu funcionamento.
Primeiramente percebemos que o Arduino IDE possui alguns exemplos integrados como um
mediador e facilitador para quem nunca teve contato com a plataforma. Para verificar a lista
completa de exemplos basta acessar a opção File > Examples.
Vale lembrar que os códigos de exemplo são escritos na [linguagem C](https://pt.wikipedia.org/wiki/C_-
(linguagem_de_programa%C3%A7%C3%A3o) e não em Javascript, mas nada impede de rodar o
código exemplo em seu sistema operacional.
27
https://github.com/arduino/Arduino/wiki/Building-Arduino
28
https://github.com/johnny-five-io/nodebots-interchange
Nodebots e microcontroladores 14

Acessando a lista de exemplos do Arduino IDE

Vamos agora conectar a nossa placa Arduino ao nosso sistema operacional. Como já conectei
anteriormente, o Arduino IDE já armazenou a configuração do meu Arduino, que aparece no item
“Board: Arduino Genuino/UNO”, mas na primeira utilização aparecerá na opção “Port”, que possui
a listagem completa das portas seriais de seu sistema operacional.
Nodebots e microcontroladores 15

Integrando a placa Arduino ao sistema operacional

O nome aparecerá com o prefixo “/dev/cu.” e possuirá o nome do Arduino, facilitando a integração.
Escolha a porta na qual o seu Arduino está conectado e pronto: a conexão foi efetuada com sucesso.

Firmata
Firmata é um protocolo para a comunicação com os microcontroladores de software em um
computador (ou smartphone / tablet, etc). O protocolo pode ser implementado no firmware de
qualquer arquitetura microcontrolador, bem como em qualquer pacote de software de computador.
O nosso próximo passo após a instalação do Arduino IDE é adicionarmos o protocolo Firmata no
nosso Arduino. Vamos abrir o nosso Arduino IDE e acessar a opção Files > Examples > Firmata >
StandardFirmata.
Nodebots e microcontroladores 16

Acessando as opções de exemplo do Firmata no Arduino IDE

Com o Arduino plugado no nosso computador rodamos o código a seguir e aguardamos a mensagem
da IDE de que tudo ocorreu com sucesso.
Nodebots e microcontroladores 17

Tudo ok. Firmata rodando

Johnny Five
Johnny-Five é um framework open source que permite que você controle um micro-controladores e
componentes utilizando funções muito similares as que seriam utilizadas se você estivesse progra-
mando apenas para a plataforma Arduino em si, porém utilizando JavaScript e implementando o
protocolo Firmata para comunicação com o software no computador host.
Isso permite que você escreva um firmware personalizado sem ter que criar o seu próprio protocolo
e objetos para o ambiente de programação que você está usando. Resumindo, Johnny-Five é um
pacote node que irá permitir programar micro controladores utilizando JavaScript!
Nodebots e microcontroladores 18

Adicionando johnny Five no projeto


Como todo bom pacote NodeJS, adicionar o Johnny-five no projeto é uma tarefa bem simples. Para
isto vamos utilizar o comando que já vimos anteriormente, o npm install, e instalaremos o johnny
five localmente, salvando como dependência de desenvolvimento do projeto.

1 $ npm install --save johnny-five

Após este comando, o NPM vai criar a pasta node_modules e dentro dela teremos o nosso pacote
johnny-five instalado e acessível no contexto do nosso projeto.
Podemos verificar também que o nosso package.json foi alterado. Nele foram adicionadas as
informações do nome do nosso pacote NodeJS e a versão instalada, como podemos ver no código a
seguir.

1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "test": "echo \"Error: no test specified\" && exit 1"
8 },
9 "author": "",
10 "license": "ISC",
11 "dependencies": {
12 "johnny-five": "^0.10.0"
13 }
14 }

Criando um Hello World


Agora que todo o setup do nosso projeto foi realizado, vamos criar o nosso código de exemplo, e
nada melhor do que o bom e velho “Hello World”, dando-lhe boas vindas ao mundo Nodebots.
Vamos criar um código simples. Primeiramente criaremos o arquivo index.js na raíz do nosso
projeto e importaremos o pacote Johnny-five utilizando o comando require.

1 ...
2 var five = require('johnny-five');
3 ...
Nodebots e microcontroladores 19

Este comando faz a requisição do pacote, tornando-o acessível ao nosso projeto. Agora conheceremos
a nossa primeira classe do Jonny Five: o Board.
A classe Board retorna para a aplicação um objeto que representa a própria placa física na eletrônica.
Todos os objetos dispositivo dependem deste objeto para serem inicializados.

1 ...
2 var board = new five.Board();
3 ...

O objeto board possui um método .on(), que é comumente usado em aplicações Javascript para
criação de event handlers. Este método aceita 2 parâmetros:

• Nome do evento;
• função a ser executada quando o evento for acionado;

Neste exemplo chamaremos este método com a opção ready, que verifica quando o código já está
acessando a placa física utilizada.

1 ...
2 board.on('ready', function() {
3 console.log('Hello World!');
4 });
5 ...

E o conteúdo final do nosso index.js ficou bem enxuto, como podem ver abaixo.

1 var five = require('johnny-five');


2 var board = new five.Board();
3
4 board.on('ready', function() {
5 console.log('Hello World!');
6 });

Agora podemos rodar o nosso código via linha de comando digitando o comando:

1 $ node index.js
Nodebots e microcontroladores 20

E o resultado retornado será a mensagem “Hello World”, como vocês podem ver na figura abaixo.

executando o comando node index.js

Caso queiram facilitar, podemos utilizar o npm start, um dos comandos NPM padrão que podemos
criar no nosso package.json, tornando-o acessível via linha de comando.

1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "start": "node index.js",
8 "dev": "echo \"This is our 'dev' NPM task\"",
9 "test": "echo \"Error: no test specified\" && exit 1"
10 },
11 "author": "",
12 "license": "ISC",
13 "dependencies": {
14 "johnny-five": "^0.10.0"
15 }
16 }

Após adicionarmos o comando, basta digitarmos na linha de comando npm start e o resultado será
a mesma mensagem do comando anterior.
Neste tópico vocês viram a integração entre o nosso código Javascript com o hardware. Nos próximos
capítulos veremos mais exemplos mostrando de uma maneira simples e divertida como integrar
Nodebots ao nosso cotidiano.
Primeiro projeto: Build Checker
O que é um build pipeline
Em algumas apresentações percebi que este termo não é conhecido por muitas pessoas, mesmo as
que são mais familiarizadas com abordagens como integração contínua e entrega contínua.
Build pipeline é um conceito que foi construído em meados de 2005 e é baseado na ideia de
paralelização de tarefas, separando cada etapa em pequenos critérios de aceitação para a aplicação.
Vale lembrar que esses passos podem automáticos ou manuais.

Criando um Build Checker


Sempre que começamos o contato com o Arduino, por exemplo, fazemos o exemplo de piscas as leds,
comumente conhecido como blink.
Neste exemplo mostrarei uma forma mais atrativa de abordar este exemplo para o nosso cotidiano,
baseado em modelos como Hubot29 e Retaliation30 para checarmos a nossa build pipeline e
averiguarmos a saúde de nossa aplicação utilizando Arduino + NodeJS + Johnny-Five em uma
introdução a NodeBots.

Anatomia de um verificador de build


O projeto foi baseado no CCmenu31 , projeto criado pelo ThoughtWorker Erik Doernenburg para
checar e mostrar o status de um determinado projeto em um servidor de integração contínua.
No nosso caso passamos a idéia para algo físico, utilizando open hardware e NodeJS. Nossa aplicação
consumirá um XML com as informações retornadas pelo Travis-CI. A partir destes dados checamos
o estado atual da aplicação e o retornaremos utilizando alguns artifícios como Arduino e LEDs para
avisar ao nosso time de que algo de errado aconteceu com o nosso build e devemos corrigir o quanto
antes.
29
https://hubot.github.com/
30
https://github.com/codedance/Retaliation
31
http://ccmenu.org/
Primeiro projeto: Build Checker 22

Material necessário
Para este projeto utilizaremos:

• 1 Protoboard: Uma protoboard nada mais é que uma placa com furos e conexões condutoras
para montagem de circuitos elétricos experimentais, sem a necessidade de soldagem. Um
protoboard simples custa entre R$ 5,00 a R$ 10,00 e pode ser encontrado em qualquer loja
de elétrica;
• 2 LEDS (light-emitting diode): 1 vermelha para sinalizar o build quebrado e 1 verde para
sinalizar o que o build foi concluído com sucesso. Uma LED custa menos de R$ 0,50 e pode
ser encontrada em qualquer loja de elétrica;
• Arduino com 2 portas GND (ground);

Material necessário para o Build Checker

Portas GND são chamadas de portas “terra”. São condutores elétricos que conectam-se à Terra - ou
seja, ao Terra Elétrico. Uma vez que encontra-se sempre neutro e (teoricamente) presente em todo
circuito elétrico, é sempre tomado como ponto de referência para a medida de potenciais, contendo
zero volts.
Agora basta plugarmos as 2 LEDS, vinculando da seguinte forma:

• Build de sucesso na porta de número 12 + uma porta GND do nosso Arduino;


• Build de erro na porta de número 10 + uma porta GND do nosso Arduino;

A imagem a seguir ilustra a montagem dos componentes com o arduino.


Primeiro projeto: Build Checker 23

Conectando o Arduino: portas e LEDS

Controlando a LED
Conhecendo os componentes que utilizaremos, vamos agora ao nosso código inicial que vai controlar
a nossa LED. Primeiramente criaremos a pasta src, onde ficará o código de nossa aplicação.
Vamos criar a pasta do nosso projeto build-checker e navegarmos para a pasta criada.

1 $ mkdir build-checker
2 $ cd build-checker

Iniciaremos a nossa aplicação digitando o comando npm init com a flag -y que significa que todas
as respostas que foram feitas anteriormente serão respondidas e cadastradas com a resposta padrão.
Após isto instalaremos o pacote johnny-five localmente como uma dependência na pasta do nosso
projeto.
Primeiro projeto: Build Checker 24

1 $ npm init -y
2 $ npm install --save johnny-five

Dentro da pasta do nosso projeto criaremos a pasta src e dentro dela o nosso arquivo index.js,
onde ficará o nosso conteúdo.

1 $ mkdir src
2 $ touch src/index.js

No arquivo index.js e importaremos o pacote Johnny-five utilizando o comando require e


criaremos a nossa instância de protoboard para adicionarmos o LED.

1 ...
2 var five = require('johnny-five');
3 var board = new five.Board();
4 ...

Para a primeira atividade com o componente utilizaremos uma nova classe do Johnny Five chamada
LED. Para utilizarmos esta classe precisamos passar o valor do pino que o LED está conectado ao
Arduino.

1 ...
2 var led = new five.Led(12);
3 ...

O nosso primeiro código funcional será mais ou menos desta forma.

1 var five = require('johnny-five');


2 var board = new five.Board();
3
4 board.on('ready', function() {
5 var ledSuccess = new five.Led(12);
6 var ledError = new five.Led(10);
7
8 ledSuccess.blink();
9 ledError.blink();
10 });

Agora vamos colocar o nosso código para funcionar no nosso Arduino. Dentro da pasta do nosso
projeto, vamos digitar no nosso prompt de comando/terminal o seguinte comando:
Primeiro projeto: Build Checker 25

1 $ node src/index.js

Após este comando o nosso prompt/linha de comando está mostrando que o comando rodou com
sucesso e o resultado será que as nossas duas LEDs estarão piscando.
Bastante simples, não é mesmo? No próximo tópico vamos pensar um pouco mais sobre a nossa
arquitetura.

Criando a requisição das informações de build no


CI/CD
Já temos a nossa abstração do build checker, vamos agora adicionar a última funcionalidade que
é a da leitura das informações de build no nosso servidor de entrega contínua e/ou servidor de
integração contínua.
A partir daí criamos uma requisição para ler o conteúdo via GET no SNAP-CI32 , o nosso serviço de
integração contínua. O SNAP-CI utiliza um conceito de build pipeline que é bem interessante e um
dos prós dele é o de feedback mais rápido, dando a possibilidade de paralelismo ou não, e definição
de etapas para o build total. Para maiores informações sobre Build Pipeline recomendo a leitura do
artigo de Continuous Integration do Martin Fowler33 .
Vamos no site do SNAP-CI, efetuamos o login e cadastramos um projeto. Caso não tenha cadastro
você terá que criar um, mas é bem rápido.

Adicionando repositórios no Snap-CI

Ao cadastrar o projeto ele vai aparecer na parte superior, à direita, um campo com o nome “CCTray”
que, ao clicarmos, direciona para o arquivo XML com as informações do build.
32
https://snap-ci.com
33
http://www.martinfowler.com/articles/continuousIntegration.html
Primeiro projeto: Build Checker 26

Visualizando pipelines criadas no Snap-CI

E estas são as informações que consultaremos com nosso build-checker.

1 <Projects>
2 <Project
3 name="brasil-de-fato/news-service (master) :: CONTRACT-TEST"
4 activity="Sleeping"
5 lastBuildLabel="77"
6 lastBuildStatus="Success"
7 lastBuildTime="2016-01-21T18:46:48Z"
8 webUrl="https://snap-ci.com/brasil-de-fato/news-service/branch/master/logs/defau\
9 ltPipeline/77/CONTRACT-TEST"/>
10 </Projects>

Analisando as informações percebemos que a única informação que devemos validar para o nosso
build são os dados do campo lastBuildStatus. Nele é retornado se o build foi finalizado com sucesso,
finalizado com sucesso ou está acontecendo no momento da validação.
O campo lastBuildStatus pode conter:

• Success: a última tarefa no servidor foi finalizada com sucesso;


• Failure: a última tarefa no servidor foi finalizada com erro;
• Pending: a tarefa está acontecendo neste exato momento no servidor;
• Exception e Unknown: algo inesperado ocorreu com a atual tarefa no servidor. Os motivos são
dos mais diversos, como o servidor teve uma oscilação no meio da tarefa ou ele nunca rodou
uma determinada tarefa ainda, por isso não possui as informações;

Vamos agora adicionar a nossa URL que contém as informações do CCTray do nosso projeto e
criar com a requisição destas informações. Para isso vamos adicionar o pacote NodeJS request34 ,
34
https://github.com/request/request
Primeiro projeto: Build Checker 27

um cliente HTTP que foi desenvolvido com o intuito de facilitar a criação de requisições HTTP ou
HTTPS. Para adicionarmos o novo pacotes vamos digitar o seguinte comando.

1 $ npm install --save request

A utilização do request é bem simples, aceitando 2 parâmetros sendo o primeiro a URL e


o segundo a função que vai manipular a informação requisitada. Para maiores detalhes
acesse a documentação completa do pacote request no Github35 .

Agora vamos adicionar o pacote no nosso projeto e criarmos a requisição usando o módulo
RequestJS.

1 ...
2 var request = require('request');
3 var five = require('johnny-five');
4 var board = new five.Board();
5 ...

E vamos adicionar o endereço co CCTray que copiamos do nosso servidor de integração contínua
no nosso código e criaremos a requisição HTTP para leitura das informações.

1 // Para fins didáticos este código está utilizando este formato


2 // Por questões de recurso computacional utilize a URL como string
3 // ao invés da manipulação do Array no projeto final
4 var CI_CCTRACKER_URL = [
5 'https://snap-ci.com',
6 'willmendesneto',
7 'generator-reactor',
8 'branch',
9 'master',
10 'cctray.xml'
11 ].join('/');

Vamos explicar um pouco sobre o callback do RequestJS. Ele retorna 3 parâmetros:

• error: um objeto com as informações do erro que aconteceu. Caso a requisição não retorne
nenhum erro ele possui o valor padrão null;
• response: objeto com as informações do response da requisição;
• body: String com as informações do body do retorno da requisição;

No nosso código então vamos analisar o retorno da requisição e criar os devidos tratamentos. O
primeiro tratamento será a verificação do erro e, caso tenhamos um erro trataremos o erro.
35
https://github.com/request/request#table-of-contents
Primeiro projeto: Build Checker 28

1 ...
2 request(CI_CCTRACKER_URL, function(error, response, body) {
3 if (error) {
4 console.log('Something is wrong in our CI/CD =(');
5 return;
6 }
7 ...
8 });

Caso o response não retorne nenhum erro, trataremos a mensagem para ligarmos e desligarmos as
LEDs para o feedback visual.

1 ...
2 if(body.indexOf('Failure') !== -1) {
3 console.log('Your CI/CD is broken! Fix it!!!!');
4 ledSuccess.off();
5 ledError.on();
6 } else {
7 console.log('Your CI/CD is ok!');
8 ledSuccess.on();
9 ledError.off();
10 }
11 ...

Com a requisição criada, vamos somente criar um intervalo entre cada requisição, utilizando um
simples setInterval. Vamos utilizar um tempo de 500 milisegundos para validação do código, mas
este valor pode ser alterado para o que for ideal ao seu caso.

1 setInterval(function(){
2 request(CI_CCTRACKER_URL, function(error, response, body) {
3 ..
4 });
5 }, 500);

E o conteúdo final do nosso src/index.js ficou da seguinte maneira:


Primeiro projeto: Build Checker 29

1 var request = require('request');


2 var five = require('johnny-five');
3 var board = new five.Board();
4
5 // Para fins didáticos este código está utilizando este formato
6 // Por questões de recurso computacional utilize a URL como string
7 // ao invés da manipulação do Array no projeto final
8 var CI_CCTRACKER_URL = [
9 'https://snap-ci.com',
10 'willmendesneto',
11 'generator-reactor',
12 'branch',
13 'master',
14 'cctray.xml'
15 ].join('/');
16
17 board.on('ready', function() {
18
19 var ledSuccess = new five.Led(12);
20 var ledError = new five.Led(10);
21
22 setInterval(function(){
23 request(CI_CCTRACKER_URL, function(error, response, body) {
24 if (error) {
25 console.log('Something is wrong in our CI/CD =(');
26 return;
27 }
28
29 if(body.indexOf('Failure') !== -1) {
30 console.log('Your CI/CD is broken! Fix it!!!!');
31 ledSuccess.off();
32 ledError.on();
33 } else {
34 console.log('Your CI/CD is ok!');
35 ledSuccess.on();
36 ledError.off();
37 }
38
39 });
40 }, 500);
41 });

Ele consultará os dados em um intervalo previamente configurado e verifica o estado atual do


Primeiro projeto: Build Checker 30

build, baseado nas informações de todas as pipelines. Caso não haja a palavra “Failure” no response
da requisição, algo de errado aconteceu e o nosso build checker irá acender a luz vermelha, caso
contrário a luz verde continua acesa, sinalizando que está tudo ok.

Ajustando a arquitetura de nossa aplicação


Agora que finalizamos a primeira etapa e vimos o nosso funcionando com os nossos componentes,
vamos pensar em melhorar a nossa arquitetura.
Podemos ver que temos alguns valores meio que soltos, que não dizem muita coisa se não acessamos
a documentação do framework Johnny Five, tais como os números 12 e 10. Para estas e outras
configurações vamos criar um arquivo de nome configuration.js com todas as informações do
projeto.
O conteúdo inicial deste ficará arquivo será:

1 // src/configuration.js
2 module.exports = {
3 LED: {
4 SUCCESS: 12,
5 ERROR: 10
6 },
7 CI_CCTRACKER_URL: 'https://snap-ci.com/willmendesneto/generator-reactor/branch\
8 /master/cctray.xml',
9 INTERVAL: 1000
10 };

Agora vamos alterar o nosso src/index.js para utilizar o nosso arquivo com as configurações
padrão da nossa aplicação

1 var five = require('johnny-five');


2 var CONFIG = require('./configuration');
3 var board = new five.Board();
4
5 board.on('ready', function() {
6
7 var ledSuccess = new five.Led(CONFIG.LED.SUCCESS);
8 var ledError = new five.Led(CONFIG.LED.ERROR);
9
10 setInterval(function(){
11 request(CONFIG.CI_CCTRACKER_URL, function(error, response, body)
12 {
Primeiro projeto: Build Checker 31

13 ...
14 });
15 }, CONFIG.INTERVAL);
16 });

O nosso código está começando a ficar um pouco mais expressivo, concordam? Mas ainda tem algo
que podemos melhorar? Claro que sim!
Estamos falando sobre o build checker, mas não temos nenhuma abstração para esta operação.
A idéia é que o nosso código final seja somente uma inicialização do nosso app, com todas as
informações relevantes dentro desta abstração.
Vamos então criar o nosso arquivo com as abstrações de informações de LED e da requisição HTTP,
acessando as configurações.

1 var CONFIG = require('./configuration');


2 var request = require('request');
3 var five = require('johnny-five');
4
5 intervalId = null;
6 function BuildChecker() {
7 this.ledSuccess = new five.Led(CONFIG.LED.SUCCESS);
8 this.ledError = new five.Led(CONFIG.LED.ERROR);
9 };
10
11 BuildChecker.prototype.stopPolling = function() {
12 clearInterval(intervalId);
13 };
14
15 BuildChecker.prototype.startPolling = function() {
16 var self = this;
17
18 intervalId = setInterval(function(){
19 request.get(CONFIG.CI_CCTRACKER_URL, function(error, response, body) {
20 if (error) {
21 console.log('Somethink is wrong with your CI =(');
22 return;
23 }
24
25 if(body.indexOf('Success') !== -1) {
26 console.log('Your CI is ok!');
27 self.ledSuccess.on();
28 self.ledError.off();
Primeiro projeto: Build Checker 32

29 } else {
30 console.log('Somethink is wrong with your CI =(. Fix it!!!!');
31 self.ledSuccess.off();
32 self.ledError.on();
33 }
34
35 });
36 }, CONFIG.INTERVAL);
37 };
38
39 module.exports = BuildChecker;

E o nosso arquivo src/index.js irá somente invocar e iniciar o nosso código para que as LEDs
comecem a piscar.

1 var BuildChecker = require('./build-checker');


2 var five = require('johnny-five');
3 var board = new five.Board();
4
5 board.on('ready', function() {
6 buildChecker = new BuildChecker();
7 buildChecker.startPolling();
8 });

Finalizando esta separação de conceitos, melhoramos a legibilidade, manutenibilidade e várias outra


variantes de nossa aplicação. Vale ressaltar que esta é uma boa prática e que, ao decorrer do livro,
sempre estaremos pensando em melhorias do nosso código final.

Criando testes unitários para o build checker


Desta vez algo simples, mas sem uma boa informação sobre ele é como adicionar testes de unidade
em aplicativos Nodebots. Teste unitário não é algo novo, mas você não encontra conteúdo sobre
este tema em Arduino, robôs e aplicativos de hardware aberto facilmente, por isto abordaremos um
pouco sobre este tópico neste livro.
Teste unitário é apenas uma das várias maneiras de testar o seu software e ter uma confiabilidade no
produto final. Com base na pirâmide de testes36 , esta é a maneira que você deve organizar os testes
de sua aplicação.
36
http://martinfowler.com/bliki/TestPyramid.html
Primeiro projeto: Build Checker 33

Pirâmide de testes

Falaremos apenas em testes de unidade, se você gostaria de saber mais sobre todas as camadas, leia
o post “TestPyramid”37 de Martin Fowler.
A idéia do teste unitário é validar e certificar que seu código está fazendo o que ele se propõe a fazer,
dando o feedback sobre os erros rapidamente antes de efetuarmos o deploy do nosso projeto para
produção.
Um aspecto que ninguém explica muito bem é sobre os testes em Nodebots, que tem como
objetivo principal neste caso criar as simulações elétricas e com mocks e stubs simulando assim
a comunicação entre componentes.
Vamos agora criar uma pasta para os nossos testes unitários com o nome test.

1 $ mkdir test

Os testes unitários utilizarão o framework de teste MochaJS38 , SinonJS39 para spies, stubs e
mocks e ShouldJS40 para as assertions. Vamos então instalar estes pacotes como dependência de
desenvolvimento do projeto.
37
http://martinfowler.com/bliki/TestPyramid.html
38
https://mochajs.org
39
http://sinonjs.org
40
https://shouldjs.github.io
Primeiro projeto: Build Checker 34

1 $ npm install --save-dev mocha sinon should

Um pacote NodeJS fundamental nesta etapa é o mock-Firmata41 , criado por Rick Waldron para fazer
o setup dos testes em aplicações Johnny-Five mais fácil. A integração é muito simples, você só precisa
carregar e criar seu componente fake da placa no teste, como podemos ver no nosso arquivo de setup
dos testes test/spec-helper.js.

1 require('should');
2 var mockFirmata = require('mock-firmata');
3 var five = require('johnny-five');
4
5 var board = new five.Board({
6 io: new mockFirmata.Firmata(),
7 debug: false,
8 repl: false
9 });

Vamos então adicionar um teste simples para checar a integração dos nossos testes. Primeiramente
criaremos um arquivo com algumas configurações do MochaJS dentro da pasta test. Este será o
conteúdo inicial do nosso mocha.opts.

1 --reporter spec
2 --recursive
3 --require test/spec-helper.js
4 --slow 1000
5 --timeout 5000

Uma explicação rápida sobre as informações de configuração utilizadas:


--reporter spec: Tipo de reporter utilizado para mostrar as mensagens das informações dos testes;
--recursive: flag para identificar que os testes devem rodar de maneira recursiva dentro da pasta;
--require test/spec-helper.js: arquivo de setup a ser carregado antes de rodarmos os testes unitários;
--slow 1000: Tempo máximo em milissegundos de tolerância entre os testes. Caso ultrapasse este
tempo será mostrado o tempo total daquele teste com uma cor diferenciada para que possamos
efetuar as devidas alterações; --timeout 5000: Tempo máximo em milissegundos de tolerância para
a finalização de cada asserção. Caso ultrapasse este tempo os nossos testes retornarão com uma
mensagem de erro;
Criaremos um arquivo com o nome test/index.js com uma asserção bastante simples.

41
https://github.com/rwaldron/mock-firmata
Primeiro projeto: Build Checker 35

1 describe('Test validation', function() {


2 it('1 + 1 = 2', function(){
3 (1 + 1).should.be.equal(2);
4 });
5 });

Os nossos testes utilizam os métodos de escopo describe e it. O describe é utilizado agrupar
cenários, no nosso caso o Test validation. Já o it é a identificação de um dos pontos de teste.
Percebam também que temos o método should.be.equal que vai comparar se o primeiro valor é
igual ao segundo.

Configurando a suite de testes no repositório

Vamos então ao nosso prompt/terminal e vamos digitar o seguinte comando.

1 $ ./node_modules/bin/mocha

Veremos então estas informações no nosso prompt/terminal e o nosso setup inicial foi um sucesso!
Agora sim, vamos criar os cenários para os nossos testes. Vamos então definir os cenários que
devemos cobrir nos nossos testes:

• Informações iniciais quando criamos a instância do BuildChecker;


• Quando iniciamos o polling e o servidor envia informações de um build finalizado com
sucesso;
• Quando iniciamos o polling e o servidor envia informações de um build finalizado com falhas;
Primeiro projeto: Build Checker 36

• Quando paramos o nosso polling e não faremos mais requisições de dados para o nosso
servidor;

Uma forma de validarmos quando o build checker deve piscar o LED é criarmos um stub para a
solicitação utilizando o node-request para validar a resposta por tipos esperados (success e error)
e usaremos alguns spies para os LEDs.
Para esta simulação vamos criar algumas fixtures com o modelo das respostas do servidor para
sucesso e erro. Vamos então criar a nossa pasta com os dados dentro de nossa pasta que contém os
nossos testes.

1 $ mkdir test/fixtures
2 $ touch test/fixtures/success.xml test/fixtures/error.xml

E vamos adicionar as informações de cada arquivo.

1 <!-- test/fixtures/error.xml -->


2 <Projects>
3 <Project
4 name="Error-project"
5 activity="Sleeping"
6 lastBuildLabel="22"
7 lastBuildStatus="Failure"
8 lastBuildTime="2016-01-04T02:20:25Z"
9 webUrl="https://google.com"/>
10 </Projects>

1 <!-- test/fixtures/success.xml -->


2 <Projects>
3 <Project
4 name="Success-project"
5 activity="Sleeping"
6 lastBuildLabel="36"
7 lastBuildStatus="Success"
8 lastBuildTime="2016-03-22T21:09:02Z"
9 webUrl="https://google.com"/>
10 </Projects>

Vamos então criar o cenário para validar o nosso código. Algumas coisas devemos ter em mente
sobre o nosso framework de teste unitário é que o seu método beforeEach, que acontece antes de
cada método it. Utilizaremos como documentação de cada etapa que deve ocorrer para reproduzir
o cenário específico.
Vamos então explicar mais sobre o conteúdo deste arquivo e o porquê de cada teste. Criamos os
testes da instância do nosso BuildChecker e seus atributos iniciais.
Primeiro projeto: Build Checker 37

1 var BuildChecker = require('../src/build-checker');


2 var five = require('johnny-five');
3 var request = require('request');
4 var sinon = require('sinon');
5
6 describe('BuildChecker', function() {
7
8 beforeEach(function(){
9 buildChecker = new BuildChecker();
10 });
11
12 it('should have the led success port configured', function(){
13 (buildChecker.ledSuccess instanceof five.Led).should.be.equal(true);
14 });
15
16 it('should have the led error port configured', function(){
17 (buildChecker.ledError instanceof five.Led).should.be.equal(true);
18 });
19
20 });

Agora vamos validar quando paramos o nosso polling. Vamos usar agora o método spy do sinon
para verificar se o código utilizou o método clearInterval para finalizar com as requisições. Para isso
verificaremos se o global.clearInterval foi utilizado uma vez, acessando o boolean calledOnce,
que é um contador interno adicionado pelo método sinon.spy para os testes.

1 ...
2 describe('#stopPolling', function(){
3 beforeEach(function(){
4 sinon.spy(global, 'clearInterval');
5 buildChecker.stopPolling();
6 });
7
8 it('should remove interval', function(){
9 global.clearInterval.calledOnce.should.be.true;
10 });
11 });
12 ...

E agora os cenários do servidor respondendo com sucesso e falha. Para isto vamos atribuir os nossos
dados da pasta fixtures em variáveis.
Primeiro projeto: Build Checker 38

1 ...
2 var fs = require('fs');
3 var successResponseCI = fs.readFileSync(__dirname + '/fixtures/success.xml', 'ut\
4 f8');
5 var errorResponseCI = fs.readFileSync(__dirname + '/fixtures/error.xml', 'utf8');
6 ...

Perceba que em cada um dos casos de sucesso e falha estamos utilizando o método sinon.useFakeTimers,
que é uma maneira de simular eventos vinculados a objetos timer no Javascript.
Quando chamamos o método clock.tick com a informação contida no arquivo de configuração,
simulamos alterações de horário e tempo no momento do teste, o que nos ajuda a forçar a chamada
do evento de polling, que utiliza o setInterval.

1 ...
2 clock = sinon.useFakeTimers();
3 clock.tick(CONFIG.INTERVAL);
4 ...

Utilizamos agora o método stub do sinon para o pacote node-request. Com isso podemos alterar
o retorno quando o método request.get for chamado. Neste caso podemos simular o response de
cada requisição, com base nas informações das nossas fixtures.

1 ...
2 sinon.stub(request, 'get').yields(null, null, successResponseCI);
3 ...

Um ponto importante nos nossos testes é lembrar de restaurar todas as informações de stub e
do fakeTimers. Utilizaremos o afterEach, método que é chamado sempre após cada método it,
e adicionaremos os nossos objetos chamando o método restore adicionado pelo sinon.

1 ...
2 afterEach(function(){
3 request.get.restore();
4 clock.restore();
5 });
6 ...

Com base nos nossos cenários de teste, este é o conteúdo do teste do nosso arquivo build checker:
Primeiro projeto: Build Checker 39

1 // test/build-checker.js
2
3 var BuildChecker = require('../src/build-checker');
4 var CONFIG = require('../src/configuration');
5 var five = require('johnny-five');
6 var request = require('request');
7 var sinon = require('sinon');
8 var fs = require('fs');
9 var successResponseCI = fs.readFileSync(__dirname + '/fixtures/success.xml', 'ut\
10 f8');
11 var errorResponseCI = fs.readFileSync(__dirname + '/fixtures/error.xml', 'utf8');
12 var clock = null;
13
14 describe('BuildChecker', function() {
15
16 beforeEach(function(){
17 buildChecker = new BuildChecker();
18 });
19
20 it('should have the led success port configured', function(){
21 (buildChecker.ledSuccess instanceof five.Led).should.be.equal(true);
22 });
23
24 it('should have the led error port configured', function(){
25 (buildChecker.ledError instanceof five.Led).should.be.equal(true);
26 });
27
28 describe('#stopPolling', function(){
29 beforeEach(function(){
30 sinon.spy(global, 'clearInterval');
31 buildChecker.stopPolling();
32 });
33
34 it('should remove interval', function(){
35 global.clearInterval.calledOnce.should.be.true;
36 });
37 });
38
39 describe('#startPolling', function(){
40 beforeEach(function(){
41 sinon.spy(global, 'setInterval');
42 buildChecker.startPolling();
Primeiro projeto: Build Checker 40

43 });
44
45 afterEach(function(){
46 global.setInterval.restore();
47 });
48
49 it('should creates polling', function(){
50 global.setInterval.calledOnce.should.be.true;
51 });
52
53 describe('When the CI server send success response', function(){
54 beforeEach(function() {
55 clock = sinon.useFakeTimers();
56 sinon.stub(request, 'get').yields(null, null, successResponseCI);
57 sinon.spy(buildChecker.ledSuccess, 'on');
58 sinon.spy(buildChecker.ledError, 'off');
59 buildChecker.startPolling();
60 clock.tick(CONFIG.INTERVAL);
61 });
62
63 afterEach(function(){
64 request.get.restore();
65 clock.restore();
66 });
67
68 it('should turn on the success led', function(){
69 buildChecker.ledSuccess.on.calledOnce.should.be.true;
70 });
71
72 it('should turn off the error led', function(){
73 buildChecker.ledError.off.calledOnce.should.be.true;
74 });
75
76 });
77
78 describe('When the CI server send error response', function(){
79 beforeEach(function() {
80 clock = sinon.useFakeTimers();
81 sinon.stub(request, 'get').yields(null, null, errorResponseCI);
82 sinon.spy(buildChecker.ledError, 'on');
83 sinon.spy(buildChecker.ledSuccess, 'off');
84 buildChecker.startPolling();
Primeiro projeto: Build Checker 41

85 clock.tick(CONFIG.INTERVAL);
86 });
87
88 afterEach(function(){
89 request.get.restore();
90 clock.restore();
91 });
92
93 it('should turn off the success led', function(){
94 buildChecker.ledSuccess.off.calledOnce.should.be.true;
95 });
96
97 it('should turn on the error led', function(){
98 buildChecker.ledError.on.calledOnce.should.be.true;
99 });
100
101 });
102
103 });
104
105 });

Este é apenas um dos vários formatos de testes unitários para a sua aplicação. Com isso terminamos
o nosso primeiro projeto com testes unitários baseado nos nossos possíveis cenários, mas caso queira
fazer download ou fork do código final, acesse o repositório do projeto build checker no Github42 .
Vamos para o nosso próximo projeto com Nodebot e Johnny-five?
42
https://github.com/willmendesneto/build-checker
Segundo projeto: Alarme de incêndio
O nosso segundo projeto de exemplo será o de um alarme de incêndio inteligente. O nosso alarme
de incêndio verificará pela temperatura e, em caso de incêndio, acionará o alarme sonoro e enviará
um SMS para o celular cadastrado.
Um exemplo simples, mas que mostra alguns pontos de integração interessantes, como integração
com API’s, leitura de dados de um sensor de temperatura e integração com o sensor sonoro Piezo.

Anatomia de um alarme de incêndio


O projeto foi baseado em um sistema domiciliar simples de alarme de incêndio. Por padrão sistemas
deste tipo fazem uma verificação de tempos em tempos e ativam o alarme de algum padrão anormal
de temperatura é encontrado.

Material necessário
Para este projeto utilizaremos:

• 1 Protoboard: Uma protoboard nada mais é que uma placa com furos e conexões condutoras
para montagem de circuitos elétricos experimentais, sem a necessidade de soldagem;
• 1 sensor de alarme Piezo: Ele será utilizado para o feedback sonoro ao usuário final, no nosso
caso ao(s) inquilino(s) do imóvel. Este sensor é bem simples e custa menos de R$ 2,00 podendo
ser encontrado em qualquer loja de elétrica;
• 1 sensor de temperatura: O Johnny-Five trabalha os mais diversos sensores de temperatura.
Neste caso usaremos o sensor de temperatura LM35. Este sensor é bem simples e custa menos
de R$ 1,50 podendo ser encontrado em qualquer loja de elétrica;
Segundo projeto: Alarme de incêndio 43

Sensores de piezo e temperatura

• Para obter a lista completa dos sensores suportados acesse a página Wiki do Johnny para
sensores de temperatura43 e verifique a lista dos códigos de referência dos sensores.

Alguns sensores necessitam de uma porta de voltagem específica para o seu correto funcionamento
(alguns sensores chamam de VND). No nosso caso usaremos uma porta de 5 volts para o sensor de
temperatura.
Outros sensores podem precisar de uma porta analógica. Portas analógicas são utilizadas para
que o sensor envie dados de voltagem para o Arduino e assim possamos ler e interpretar as suas
informações.
Para este projeto vamos montar os sensores no Arduino da seguinte maneira:

• Sensor de alarme piezo: encaixe o fio preto do piezo na porta GND e o fio vermelho na porta
de número 3 do nosso arduino;
• Sensor de temperatura LM35: encaixe o pino de aterramento na porta GND, o pino de voltagem
na porta de 5 volts e o pino de saída de dados na porta analógica “A0”;

A imagem a seguir ilustra a montagem dos componentes integrados com o Arduino.


43
https://github.com/rwaldron/johnny-five/wiki/thermometer
Segundo projeto: Alarme de incêndio 44

Integração dos componentes utilizados no Fire Alarm


Segundo projeto: Alarme de incêndio 45

Controlando o sensor de chamas


Com o sensor LM35 conectado à placa Arduino, vamos agora fazer a leitura da informação
da temperatura do ambiente. Para isso utilizaremos a classe Thermometer do Johnny five. Ao
instanciarmos um novo objeto Thermometer, devemos nos atentar a alguns parâmetros:

• controller: Nome do sensor utilizado. Você pode consultar a lista completa dos sensores
suportados pelo Johnny Five na documentação da classe Thermometer44 ;
• pin: A informação do pino utilizado para a conexão analógica no Arduino. Ele é utilizado em
sensores analógicos para leitura da informação de temperatura;
• toCelsius: Um método opcional que podemos reescrever para lidar com o dado analógico e
transformá-lo no formato de temperatura de sua preferência. No nosso caso utilizaremos o
formato Celsius;

Baseado nestas informações, o arquivo principal será do nosso alarme de incêndio será:

1 var five = require('johnny-five');


2 var board = new five.Board();
3
4 function FireAlarm() {
5 return new five.Thermometer({
6 controller: "LM35",
7 pin: "A0"
8 });
9 };
10
11 board.on("ready", function() {
12 var temperatureSensor = new FireAlarm();
13
14 setInterval(function() {
15 console.log("celsius: %d", temperatureSensor.celsius);
16 });
17 });

Vamos então validar a funcionalidade do nosso código com a plataforma Arduino. Dentro da pasta
do nosso projeto, vamos digitar no nosso prompt de comando/terminal o seguinte comando:

1 $ node src/index.js
44
https://github.com/rwaldron/johnny-five/wiki/thermometer
Segundo projeto: Alarme de incêndio 46

E este será o resultado do nosso código.

lendo informações de temperatura do sensor usando javascript

O código é bastante simples, como podem perceber. No próximo tópico vamos pensar um pouco
mais sobre a nossa arquitetura e como evoluir este código para algo mais fácil de manter.

Evoluindo o nosso código inicial


O nosso código inicial está funcional, mas evoluir este código para os próximos passos é algo
complexo. Para facilitarmos as próximas etapas do nosso projeto, vamos fazer algumas adaptações
no nosso código inicial.
Vocês podem perceber que temos várias configurações, tais como o nome do controller e pin do
sensor. Para melhorarmos o manusear destas configurações que são constantes, ou seja, não mudam
durante todo o tempo de vida da nossa aplicação, vamos adicioná-las em um arquivo com as nossas
configurações específicas.
Este será adicionado ao nosso arquivo src/configuration.js

1 // src/configuration.js
2 module.exports = {
3 FIRE_ALARM: {
4 // https://github.com/rwaldron/johnny-five/wiki/thermometer
5 CONTROLLER: "LM35",
6 PIN: 'A0'
7 },
8 INTERVAL: 1000
9 };

Este é o conteúdo do nosso arquivo src/fire-alarm.js. Percebam que agora estamos invocando
o código de configuração externo e adicionando os valores na variável CONFIG. Esta etapa é
interessante, pois desvinculamos as configurações básicas do nosso projeto da classe, que agora
possui a responsabilidade de lidar com os sensores do projeto fire alarm.
Segundo projeto: Alarme de incêndio 47

1 // src/fire-alarm.js
2 var CONFIG = require('./configuration');
3 var five = require('johnny-five');
4 var intervalId = null;
5
6 function FireAlarm() {
7 this.temperatureSensor = new five.Thermometer({
8 controller: CONFIG.FIRE_ALARM.CONTROLLER,
9 pin: CONFIG.FIRE_ALARM.PIN
10 });
11 };
12
13 FireAlarm.prototype.stopPolling = function() {
14 clearInterval(intervalId);
15 };
16
17 FireAlarm.prototype.startPolling = function() {
18 self = this;
19 intervalId = setInterval(function() {
20 console.log('celsius: %d', self.temperatureSensor.celsius);
21 }, CONFIG.INTERVAL);
22 };
23
24 module.exports = FireAlarm;

E o nosso arquivo src/index.js principal vai ter um conteúdo mais simples, tendo a responsabili-
dade de iniciar o projeto e o polling da instância da classe FireAlarm.

1 // src/index.js
2 var FireAlarm = require('./fire-alarm');
3 var five = require('johnny-five');
4 var board = new five.Board();
5
6 board.on('ready', function() {
7 fireAlarm = new FireAlarm();
8 fireAlarm.startPolling();
9 });

Quando executamos o nosso código a partir do comando, veremos o mesmo resultado na nossa linha
de comando/prompt de comando.
Segundo projeto: Alarme de incêndio 48

1 $ npm start

Com essas alterações temos um código de fácil manutenção e muito mais legibilidade para ser
utilizado na nossa aplicação. Claro que esta é uma das várias abordagens que podem ser utilizadas
no seu projeto, mas o foco deste tópico é passar a idéia de sempre pensar em como melhorar o nosso
projeto.

Integrando com o Piezo para aviso sonoro


Twilio é um serviço que permite aos desenvolvedores incorporar voz, VoIP e mensagens SMS em
aplicativos a partir de uma API RESTful que fornece os recursos de voz e SMS para aplicativos.
O sensor de alarme que vamos utilizar é o Piezo por se tratar de um componente simples de
manipular e bastante barato, tendo o seu valor em torno de R$ 2,00.
A integração do Piezo ao nosso projeto é algo bem trivial, pois o Johnny Five já possui a classe
five.Piezo.

1 ...
2 this.piezo = new five.Piezo(3);
3 ...

Esta classe aceita o número do pino que está vinculado ao sensor e ao ativar temos o método play,
que aceita um objeto com as informações:

• tempo: intervalo entre cada nota;


• song: array que aceita arrays de 2 posições com as informações de nota musical e o tempo;

Você pode perceber um exemplo do método play sendo utilizado no código abaixo.

1 self.piezo.play({
2 song: [
3 ['G5', 1/4]
4 ],
5 tempo: 200
6 });

No nosso projeto vamos fazer a integração em dividida em 2 partes. Primeiramente vamos adicionar
a instância do piezo no construtor da nossa classe, para que o piezo fique acessível nos outros
métodos.
Segundo projeto: Alarme de incêndio 49

1 ...
2 function FireAlarm() {
3 this.piezo = new five.Piezo(3);
4 ...
5 }
6 ...

Com a nossa instância adicionada e acessível, vamos utilizá-lo no nosso método startPolling. Va-
mos adicionar mais uma validação, acessando o boolean piezo.isPlaying que contém a informação
de inicialização do piezo no nosso projeto e, caso a temperatura esteja acima do limite e o piezo esteja
acessível, acionaremos o nosso alarme sonoro. Com isto o método ficará como o código a seguir.

1 ...
2 FireAlarm.prototype.startPolling = function() {
3 self = this;
4 intervalId = setInterval(function() {
5 if (self.temperatureSensor.celsius >= CONFIG.FIRE_ALARM.LIMIT && !self.piezo\
6 .isPlaying) {
7 self.piezo.play({
8 song: [
9 ['G5', 1/4],
10 [null, 7/4]
11 ],
12 tempo: 200
13 });
14 console.log('Up to the limit:', self.temperatureSensor.celsius);
15 } else {
16 console.log('That\'s ok:', self.temperatureSensor.celsius);
17 }
18 }, CONFIG.INTERVAL);
19 };
20 ...

O nosso código final do FireAlarm terá o seguinte conteúdo:


Segundo projeto: Alarme de incêndio 50

1 var CONFIG = require('./configuration');


2 var request = require('request');
3 var five = require('johnny-five');
4 var intervalId = null;
5
6 function FireAlarm() {
7 this.piezo = new five.Piezo(3);
8 this.temperatureSensor = new five.Thermometer({
9 pin: CONFIG.FIRE_ALARM.PIN
10 });
11 };
12
13
14 FireAlarm.prototype.stopPolling = function() {
15 clearInterval(intervalId);
16 };
17
18 FireAlarm.prototype.startPolling = function() {
19 self = this;
20 intervalId = setInterval(function() {
21 if (self.temperatureSensor.celsius >= CONFIG.FIRE_ALARM.LIMIT && !self.piezo\
22 .isPlaying) {
23
24 self.piezo.play({
25 song: [
26 ['G5', 1/4],
27 [null, 7/4]
28 ],
29 tempo: 200
30 });
31
32 console.log('Up to the limit:', self.temperatureSensor.celsius);
33 } else {
34 console.log('That\'s ok:', self.temperatureSensor.celsius);
35 }
36
37 }, CONFIG.INTERVAL);
38 };
39
40 module.exports = FireAlarm;

Com isto temos o primeiro feedback para os usuários da nossa aplicação. A próxima etapa será
adicionar a funcionalidade de envio de SMS utilizando a API do Twilio.
Segundo projeto: Alarme de incêndio 51

Enviando SMS para o seu celular usando o Twilio


Twilio é um serviço que permite aos desenvolvedores incorporar voz, VoIP e mensagens SMS em
aplicativos a partir de uma API RESTful que fornece os recursos de voz e SMS para aplicativos. As
bibliotecas de cliente estão disponíveis em vários idiomas e, claro, possuem um cliente para o NodeJS
chamado twilio-node45 .
Integrar SMS no nosso aplicativo então será algo bem simples. Vamos ao site to Twilio e vamos
habilitar o serviço de SMS.

Página de setup do SMS no Twilio

Ele possui alguns números pagos, caso queira utilizar em algum produto realmente. No nosso caso
utilizaremos o número gerado pelo próprio serviço e criaremos uma conta trial na plataforma.
45
https://twilio.github.io/twilio-node
Segundo projeto: Alarme de incêndio 52

Adicionando um número de telefone no Twilio

Agora, com o Twilio configurado, vamos então começar a integração com o nosso alarme de incên-
dio. Primeiramente vamos adicionar as informações do telefone, chave SSID da sua conta e token
de autenticação do Twilio. Vamos adicionar estas informações no nosso src/configuration.js.

1 module.exports = {
2 LIMIT: 30,
3 PIN: 'A0',
4 PHONE_NUMBER: ''
5 },
6 TWILIO: {
7 PHONE_NUMBER: '',
8 ACCOUNT_SSID: '',
9 AUTH_TOKEN: ''
10 },
11 INTERVAL: 1000
12 };

E vamos continuar com a integração do Twilio no nosso código, acessando e lendo as informações
adicionadas no arquivo src/configuration.js com o pacote do Twilio node.

1 // fire-alarm.js
2
3 var CONFIG = require('./configuration');
4 var request = require('request');
5 var five = require('johnny-five');
6 var twilio = require('twilio');
7 var intervalId = null;
8
9 var client = new twilio.RestClient(CONFIG.TWILIO.ACCOUNT_SSID, CONFIG.TWILIO.AUT\
10 H_TOKEN);
11
Segundo projeto: Alarme de incêndio 53

12 function FireAlarm() {
13 this.piezo = new five.Piezo(3);
14 this.temperatureSensor = new five.Thermometer({
15 pin: CONFIG.FIRE_ALARM.PIN
16 });
17 };
18
19 FireAlarm.prototype.stopPolling = function() {
20 clearInterval(intervalId);
21 };
22
23 FireAlarm.prototype.startPolling = function() {
24 self = this;
25 intervalId = setInterval(function() {
26 if (self.temperatureSensor.celsius >= CONFIG.FIRE_ALARM.LIMIT && !self.piezo\
27 .isPlaying) {
28
29 self.piezo.play({
30 song: [
31 ['G5', 1/4],
32 [null, 7/4]
33 ],
34 tempo: 200
35 });
36
37 client.messages.create({
38 body: 'Something is wrong with your fire alarm. Please, call the local f\
39 ire brigade.',
40 to: CONFIG.FIRE_ALARM.PHONE_NUMBER,
41 from: CONFIG.TWILIO.PHONE_NUMBER
42 });
43
44 console.log('Up to the limit:', self.temperatureSensor.celsius);
45 } else {
46 console.log('That\'s ok:', self.temperatureSensor.celsius);
47 }
48 }, CONFIG.INTERVAL);
49 };
50
51 module.exports = FireAlarm;

Vamos agora executar o nosso código digitando o comando npm start e esta será a informação
impressa no nosso prompt/linha de comando.
Segundo projeto: Alarme de incêndio 54

Ao executarmos o nosso código e a temperatura exceder o limite configurado, além do sensor Piezo
que acionará o alarme o nosso cliente do Twilio será acionado e nos enviará um SMS utilizando as
configurações previamente adicionadas. Então você receberá a seguinte mensagem no seu celular.

SMS enviado pela API do Twilio

Com isto vimos a integração completa do nosso Fire Alarm. Claro que este é o primeiro passo, você
pode evoluir o código e adicionar novas funcionalidades. Como já sabemos: o céu é o limite!
Mas e quanto aos testes unitários? Sabemos que está funcionando, mas temos que ter certeza de que
o código tem um nível aceitável de qualidade até mesmo para evoluirmos o nosso projeto e adicionar
novas funcionalidades. Vamos agora criar os nossos testes unitários.

Criando testes unitários para o Fire Alarm


Como comentado no conteúdo “Criando testes unitários para o build checker”, teste unitário é apenas
uma das várias maneiras de testar o seu software e ter uma confiabilidade no produto final com
algumas validações automáticas antes de efetuarmos o deploy do nosso projeto para produção.
Vamos agora criar uma pasta para os nossos testes unitários com o nome test. Como padrão,
instaremos o framework de teste MochaJS46 , SinonJS47 para spies, stubs e mocks e ShouldJS48 para
as assertions. Vamos então instalar estes pacotes como dependência de desenvolvimento do projeto.

1 $ mkdir test
2 $ npm install --save-dev mocha sinon should

Para os nossos testes reutilizaremos o conteúdo do test/spec-helper.js e do test/mocha.opts que


criamos para o nosso projeto do build checker. Ele utiliza o pacote mock-Firmata49 para setup dos
testes na nossa aplicação Johnny-Five.

46
https://mochajs.org
47
http://sinonjs.org
48
https://shouldjs.github.io
49
https://github.com/rwaldron/mock-firmata
Segundo projeto: Alarme de incêndio 55

1 //test/spec-helper.js
2 require('should');
3 var mockFirmata = require('mock-firmata');
4 var five = require('johnny-five');
5
6 var board = new five.Board({
7 io: new mockFirmata.Firmata(),
8 debug: false,
9 repl: false
10 });

E este será o conteúdo inicial do nosso mocha.opts.

1 --reporter spec
2 --recursive
3 --require test/spec-helper.js
4 --slow 1000
5 --timeout 5000

Uma explicação rápida sobre as informações de configuração utilizadas:


–reporter spec: Tipo de reporter utilizado para mostrar as mensagens das informações dos testes;
–recursive: flag para identificar que os testes devem rodar de maneira recursiva dentro da pasta;
–require test/spec-helper.js: arquivo de setup a ser carregado antes de rodarmos os testes unitários;
–slow 1000: Tempo máximo em milissegundos de tolerância entre os testes. Caso ultrapasse este
tempo será mostrado o tempo total daquele teste com uma cor diferenciada para que possamos
efetuar as devidas alterações; –timeout 5000: Tempo máximo em milissegundos de tolerância para
a finalização de cada asserção. Caso ultrapasse este tempo os nossos testes retornarão com uma
mensagem de erro;
Agora sim, vamos criar os cenários para os nossos testes. Vamos então definir os cenários que
devemos cobrir nos nossos testes:

• Configurações iniciais da aplicação;


• Informações iniciais quando criamos a instância do FireAlarm;
• Quando iniciamos o polling e o sensor sinaliza que a temperatura está dentro do limite
aceitável;
• Quando iniciamos o polling e o sensor sinaliza que a temperatura está acima do limite
aceitável;
• Quando o sensor piezo é acionado;
• Quando a API SMS do Twilio é acionada;
Segundo projeto: Alarme de incêndio 56

Uma forma de validarmos quando o fire alarm deve acionar o alarme sonoro é criarmos um stub
para trigger e usaremos alguns spies para a API do Twilio.
Como primeiro passo criaremos os testes de nosso arquivo de configuração, que dividiremos entre
as informações do sensor de temperatura e da API do Twilio. Faremos a requisição do nosso
src/configuration.js e verificaremos a informação do pino que vai ser vinculado ao sensor, o valor
do intervalo a ser utilizado para checar as informações do sensor e o limite aceitável da temperatura
do ambiente no qual o FireAlarm estará funcionando e o telefone configurado para receber o SMS.

1 var CONFIG = require('../src/configuration');


2
3 describe('Configuration', function() {
4
5 describe('Temperature information', function() {
6
7 it('should have the sensor port configured', function(){
8 CONFIG.FIRE_ALARM.should.have.property('PIN').which.is.a.String()
9 });
10
11 it('should have the sensor alarm limit configured', function(){
12 CONFIG.FIRE_ALARM.should.have.property('LIMIT').which.is.a.Number()
13 });
14
15 it('should have the user phone configured', function(){
16 CONFIG.FIRE_ALARM.should.have.property('PHONE_NUMBER').which.is.a.String()
17 });
18 });
19 });

Agora avaliamos as informações que serão passadas para o Twilio, que no nosso caso serão a chave
SSID, token de autenticação e o número telefônico fornecido pelo Twilio.

1 ...
2 describe('SMS information', function() {
3
4 it('should have account ssid configured', function(){
5 CONFIG.TWILIO.should.have.property('ACCOUNT_SSID').which.is.a.String()
6 });
7
8 it('should have auth token configured', function(){
9 CONFIG.TWILIO.should.have.property('AUTH_TOKEN').which.is.a.String()
10 });
11
Segundo projeto: Alarme de incêndio 57

12 it('should have the user phone configured', function(){


13 CONFIG.TWILIO.should.have.property('PHONE_NUMBER').which.is.a.String()
14 });
15 });
16 ...

O nosso arquivo de testes do src/configuration.js ficará assim.

1 var CONFIG = require('../src/configuration');


2
3 describe('Configuration', function() {
4
5 describe('Temperature information', function() {
6
7 it('should have the sensor port configured', function(){
8 CONFIG.FIRE_ALARM.should.have.property('PIN').which.is.a.String()
9 });
10
11 it('should have the sensor alarm limit configured', function(){
12 CONFIG.FIRE_ALARM.should.have.property('LIMIT').which.is.a.Number()
13 });
14
15 it('should have the user phone configured', function(){
16 CONFIG.FIRE_ALARM.should.have.property('PHONE_NUMBER').which.is.a.String()
17 });
18 });
19
20 describe('SMS information', function() {
21
22 it('should have account ssid configured', function(){
23 CONFIG.TWILIO.should.have.property('ACCOUNT_SSID').which.is.a.String()
24 });
25
26 it('should have auth token configured', function(){
27 CONFIG.TWILIO.should.have.property('AUTH_TOKEN').which.is.a.String()
28 });
29
30 it('should have the user phone configured', function(){
31 CONFIG.TWILIO.should.have.property('PHONE_NUMBER').which.is.a.String()
32 });
33 });
34
Segundo projeto: Alarme de incêndio 58

35 it('should have the interval polling information', function(){


36 CONFIG.should.have.property('INTERVAL').which.is.a.Number()
37 });
38
39 });

Vamos então criar o cenário para validar o código do nosso FireAlarm. Uma das coisas que devemos
ter em mente sobre o nosso framework de teste unitário é que o seu método beforeEach, que acontece
antes de cada método it vai ser utilizado como documentação para reprodução de cada cenário
específico.
Vamos então explicar mais sobre o conteúdo deste arquivo e o porquê de cada teste. Criamos os
testes da instância do nosso FireAlarm e seus atributos iniciais.

1 var proxyquire = require('proxyquire');


2 var CONFIG = require('../src/configuration');
3 var five = require('johnny-five');
4 var request = require('request');
5 var sinon = require('sinon');
6
7 describe('FireAlarm', function() {
8
9 beforeEach(function(){
10 this.sandbox = sinon.sandbox.create();
11 this.createMessagesSpy = this.sandbox.spy();
12 var restClientMessage = {
13 messages: {
14 create: this.createMessagesSpy
15 }
16 };
17
18 twilioMock = {
19 RestClient: function() {
20 return restClientMessage
21 }
22 };
23
24 var FireAlarm = proxyquire('../src/fire-alarm', {
25 twilio: twilioMock
26 });
27 fireAlarm = new FireAlarm();
28 });
29
Segundo projeto: Alarme de incêndio 59

30 afterEach(function() {
31 this.sandbox.restore();
32 });
33
34 it('should have the termometer sensor configured', function(){
35 (fireAlarm.temperatureSensor instanceof five.Thermometer).should.be.equal(tr\
36 ue);
37 });
38
39 });

Vamos validar quando paramos o nosso polling. Vamos usar agora o método spy do sinon para
verificar se o código utilizou o método clearInterval para finalizar com as requisições. Para isso
verificaremos se o global.clearInterval foi utilizado uma vez, acessando o boolean calledOnce,
que é um contador interno adicionado pelo método sinon.spy para os testes.

1 ...
2 describe('#stopPolling', function(){
3 beforeEach(function(){
4 this.sandbox.spy(global, 'clearInterval');
5 fireAlarm.stopPolling();
6 });
7
8 it('should remove interval', function(){
9 global.clearInterval.calledOnce.should.be.true;
10 });
11 });
12 ...

E agora os cenários que acontecem quando o sensor é iniciado. Para isto vamos atribuir alguns spies
para as funções setInterval e para o sensor do piezo. A nossa primeira validação será a certificação
de que o intervalo está sendo iniciado quando invocamos o método fireAlarm.startPolling().
Segundo projeto: Alarme de incêndio 60

1 ...
2 describe('#startPolling', function(){
3 beforeEach(function(){
4 this.piezoPlaySpy = this.sandbox.spy();
5
6 var piezoStub = {
7 isPlaying: false,
8 play: this.piezoPlaySpy
9 };
10 this.sandbox.stub(fireAlarm, 'piezo', piezoStub);
11
12 this.sandbox.spy(global, 'setInterval');
13 fireAlarm.startPolling();
14 });
15
16 afterEach(function(){
17 global.setInterval.restore();
18 });
19
20 it('should creates polling', function(){
21 global.setInterval.calledOnce.should.be.true;
22 });
23 });
24 ...

Para a validação do caso da temperatura acima do limite utilizaremos o método clock.tick com
a informação contida no arquivo de configuração, simulando as alterações de horário e tempo
no momento do teste, o que nos ajuda a forçar a chamada do evento de polling, que utiliza o
setInterval.
Neste cenário utilizamos o stub para simular o retorno do sensor com o valor acima do aceitável e
nos certificamos de que o sensor Piezo e a Api do Twilio foram acionados.

1 ...
2 describe('When the temperature is up to the limit', function(){
3
4 beforeEach(function() {
5 clock = this.sandbox.useFakeTimers();
6
7 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
8 celsius: CONFIG.FIRE_ALARM.LIMIT + 1
9 });
10 fireAlarm.startPolling();
Segundo projeto: Alarme de incêndio 61

11 clock.tick(CONFIG.INTERVAL);
12 });
13
14 afterEach(function(){
15 clock.restore();
16 });
17
18 it('should trigger piezo sensor alarm', function(){
19 this.piezoPlaySpy.calledOnce.should.be.true;
20 });
21
22 it('should send the SMS to user', function() {
23 this.createMessagesSpy.calledOnce.should.be.true;
24 });
25
26 });
27 ...

Agora vamos validar o cenário da temperatura ambiente em níveis aceitáveis. As verificações


utilizam as mesmas abordagens da anterior, mas neste caso nos certifamos de que o sensor piezo
e a API do Twilio não foram acionadas.

1 ...
2 describe('When the temperature is NOT up to the limit', function(){
3
4
5 beforeEach(function() {
6 clock = this.sandbox.useFakeTimers();
7
8
9 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
10 celsius: CONFIG.FIRE_ALARM.LIMIT - 1
11 });
12
13
14 fireAlarm.startPolling();
15 clock.tick(CONFIG.INTERVAL);
16 });
17
18
19 afterEach(function(){
20 clock.restore();
Segundo projeto: Alarme de incêndio 62

21 });
22
23
24 it('should NOT trigger piezo sensor alarm', function(){
25 this.piezoPlaySpy.calledOnce.should.be.false;
26 });
27
28
29 it('should NOT send the SMS to user', function() {
30 this.createMessagesSpy.calledOnce.should.be.false;
31 });
32
33
34 });
35 ...

Com base nos nossos cenários de teste, este é o conteúdo do teste do nosso arquivo fire alarm:

1 var proxyquire = require('proxyquire');


2 var CONFIG = require('../src/configuration');
3 var five = require('johnny-five');
4 var request = require('request');
5 var sinon = require('sinon');
6
7 describe('FireAlarm', function() {
8
9 beforeEach(function(){
10 this.sandbox = sinon.sandbox.create();
11 this.createMessagesSpy = this.sandbox.spy();
12 var restClientMessage = {
13 messages: {
14 create: this.createMessagesSpy
15 }
16 };
17
18 twilioMock = {
19 RestClient: function() {
20 return restClientMessage
21 }
22 };
23
24 var FireAlarm = proxyquire('../src/fire-alarm', {
Segundo projeto: Alarme de incêndio 63

25 twilio: twilioMock
26 });
27 fireAlarm = new FireAlarm();
28 });
29
30 afterEach(function() {
31 this.sandbox.restore();
32 });
33
34 it('should have the termometer sensor configured', function(){
35 (fireAlarm.temperatureSensor instanceof five.Thermometer).should.be.equal(tr\
36 ue);
37 });
38
39 describe('#stopPolling', function(){
40 beforeEach(function(){
41 this.sandbox.spy(global, 'clearInterval');
42 fireAlarm.stopPolling();
43 });
44
45 it('should remove interval', function(){
46 global.clearInterval.calledOnce.should.be.true;
47 });
48 });
49
50 describe('#startPolling', function(){
51 beforeEach(function(){
52 this.piezoPlaySpy = this.sandbox.spy();
53
54 var piezoStub = {
55 isPlaying: false,
56 play: this.piezoPlaySpy
57 };
58 this.sandbox.stub(fireAlarm, 'piezo', piezoStub);
59
60 this.sandbox.spy(global, 'setInterval');
61 fireAlarm.startPolling();
62 });
63
64 afterEach(function(){
65 global.setInterval.restore();
66 });
Segundo projeto: Alarme de incêndio 64

67
68 it('should creates polling', function(){
69 global.setInterval.calledOnce.should.be.true;
70 });
71
72 describe('When the temperature is up to the limit', function(){
73
74 beforeEach(function() {
75 clock = this.sandbox.useFakeTimers();
76
77 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
78 celsius: CONFIG.FIRE_ALARM.LIMIT + 1
79 });
80 fireAlarm.startPolling();
81 clock.tick(CONFIG.INTERVAL);
82 });
83
84 afterEach(function(){
85 clock.restore();
86 });
87
88 it('should trigger piezo sensor alarm', function(){
89 this.piezoPlaySpy.calledOnce.should.be.true;
90 });
91
92 it('should send the SMS to user', function() {
93 this.createMessagesSpy.calledOnce.should.be.true;
94 });
95
96 });
97
98 describe('When the temperature is NOT up to the limit', function(){
99
100 beforeEach(function() {
101 clock = this.sandbox.useFakeTimers();
102
103 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
104 celsius: CONFIG.FIRE_ALARM.LIMIT - 1
105 });
106
107 fireAlarm.startPolling();
108 clock.tick(CONFIG.INTERVAL);
Segundo projeto: Alarme de incêndio 65

109 });
110
111 afterEach(function(){
112 clock.restore();
113 });
114
115 it('should NOT trigger piezo sensor alarm', function(){
116 this.piezoPlaySpy.calledOnce.should.be.false;
117 });
118
119 it('should NOT send the SMS to user', function() {
120 this.createMessagesSpy.calledOnce.should.be.false;
121 });
122 });
123 });
124 });

Com isso terminamos o nosso primeiro projeto com testes unitários, cobrindo todos os nossos
possíveis cenários, mas caso queira fazer download ou fork do código final, acesse o repositório do
projeto fire alarm no Github50 . No próximo capítulo veremos outros serviços que facilitam a nossa
vida ao enviarmos o código para produção, tais como servidores de integração contínua, cobertura
de código e de complexidade de maneira automatizada.
50
https://github.com/willmendesneto/fire-alarm
Dando suporte ao seu código em
vários sistemas operacionais
Nesta etapa do livro iremos então validar e verificar a cobertura de testes no nosso projeto em
diferentes sistemas operacionais, além de habilitarmos diferentes serviços web para melhorias de
workflow, como ferramentas de integração contínua e cobertura de código.
Esta etapa é muito importante, pois estas ferramentas nos auxiliam no processo de segurança do
nosso código, checando diferentes critérios de aceitação da nossa aplicação de maneira automatizada.

Adicionando servidores de integração contínua ao seu


projeto
Como todo projeto de qualidade, o nosso projeto Nodebots vai se preocupar em alguns outros
aspectos, como automatização da suíte de testes, build e outras tarefas relevantes ao nosso projeto.
Para isso vamos contar com o auxílio de um servidor de integração contínua. Existem vários no
mercado, sendo gratuitos ou pagos, e nesta etapa do livro vamos conhecer um pouco mais do
funcionamento e configuração de dois deles: Travis-CI e Appveyor.

Travis-CI: checando seu código no Linux e OSX


Sabendo que atualmente os sistemas operacionais mais utilizados são Unix/Linux, Windows e OSX
vamos criar verificações para cada um deles e para isto entra em cena o Travis-CI.
Ele é um dos mais famosos serviços de integração contínua51 e auxilia no processo de integração das
novas funcionalidades ou correção de bugs do código do atual projeto em vários ambientes, podendo
até mesmo efetuar o deploy para produção, caso todos os passos de validação estejam corretos.
Vamos então ao site oficial do projeto travis-ci52 e habilitar o acesso utilizando a nossa conta do
Github. Clique no botão “Sign up” e habilite acesso aos seus repositórios.
51
http://blog.caelum.com.br/integracao-continua/
52
https://travis-ci.org/
Dando suporte ao seu código em vários sistemas operacionais 67

Site do serviço Travis-CI

Após esta etapa você será redirecionado para uma nova página com todos os seus repositórios. Para
adicionarmos um novo basta clicar no ícone “+” ao lado do texto “My Repositories”.

Página de um repositório configurado no Travis-CI

Após esta etapa você será redirecionado para uma nova página com todos os seus repositórios. Para
adicionarmos um novo basta clicar no ícone “+” ao lado do texto “My Repositories”.
Esta próxima etapa é bem simples já que a página possui um tutorial mostrando cada um dos passos
para habilitar a integração do Travis-CI com o seu repositório no Github, como podemos observar
Dando suporte ao seu código em vários sistemas operacionais 68

na imagem abaixo.

Sincronizando os repositórios com o serviço

Na mesma página, será listado todos os seus repositórios para que você escolha e habilite a integração
do Travis-CI ao seu projeto. Para habilitar basta clicar no botão cinza com um “X” e quando ele mudar
a cor para verde quer dizer que esta tudo correu como esperado e o seu repositório está sincronizado
com o Travis-CI.
O Travis-CI é totalmente configurável e você pode adicionar informações das mais diversas, sendo
elas desde comandos a serem invocados antes, durante ou depois do build, podendo até mesmo
configurar os tipos de sistemas operacionais que as tarefas devem acontecer.
Estas configurações ficarão no arquivo .travis.yml que ficará na pasta raíz do nosso projeto. Vamos
explicar um pouco mais sobre como configurar estas tarefas no Travis-CI.

Ativando o webhook do travis

Primeiramente, no arquivo .travis.yml, vamos adicionar o campo "os", com as devidas informa-
ções dos sistemas operacionais utilizados para os nossos testes.
Dando suporte ao seu código em vários sistemas operacionais 69

1 ...
2 os:
3 - linux
4 - osx
5 ...

Adicionaremos também o campo "node_js", aonde ficarão as nossas informações sobre as versões
de NodeJS que as tasks deverão ser utilizados nas nossas tasks. No nosso caso adicionaremos somente
uma versão, mas poderíamos adicionar várias outras com base nas nossas necessidades de suporte,
por exemplo.

1 ...
2 node_js:
3 - '5.3.0'
4 ...

O nosso servidor de integração contínua nada mais é do que um container com um sistema
operacional completo. Sendo assim podemos também configurar variáveis de ambiente nele. Neste
caso adicionaremos a variável NO_SERIALPORT_INSTALL, especificando que não devemos instalar o
pacote “serialport” neste caso, pois trata-se de um teste que utiliza um mock de um board físico.
OBS: A idéia deste livro é de focar nos conceitos relacionados diretamente com Nodebots e inte-
grações com o repositório javascript criado, por isso não explicarei sobre o conceito de containers.
Caso queira saber mais sobre este conceito utilizado pelo Travis-CI, acesse a página oficial do projeto
Docker53 .

1 ...
2 env:
3 - NO_SERIALPORT_INSTALL=1
4 ...

Podemos também definir o conjunto de tarefas que serão utilizados antes e depois do nosso script do
travis. Neste caso utilizaremos before para os comandos que devem ocorrer antes do nosso script
principal e after para os comandos que devem ocorrer depois dos comandos travis, como vocês
podem visualizar no trecho de código a seguir:

53
https://www.docker.com
Dando suporte ao seu código em vários sistemas operacionais 70

1 ...
2 before_script:
3 - 'npm install'
4
5
6 after_script:
7 - 'make test'
8 ...

Neste caso estamos instalando as nossas dependências e rodando os nossos testes. Tudo isto de
uma maneira bem simples e bem definida. O conteúdo do nosso arquivo .travis.yml com todas
as alterações será o seguinte:

1 language: node_js
2 os:
3 - linux
4 - osx
5 node_js:
6 - '5.3.0'
7 before_script:
8 - 'npm install'
9
10
11 after_script:
12 - 'make test'
13 env:
14 - NO_SERIALPORT_INSTALL=1

Podemos perceber que o build do Travis-CI agora é um pouco diferente, pois estamos rodando o
mesmo setup nos sistemas operacionais Linux e OSX, identificados pelos ícones de cada sistema
operacional.

Lista de sistemas operacionais utilizados

Com a integração testada, vamos então colocar o badge do travis-ci no nosso arquivo README.md do
repositório. Com isto você verá uma imagem com o status do build.
Dando suporte ao seu código em vários sistemas operacionais 71

1 [![Build Status](https://travis-ci.org/willmendesneto/build-checker.png?branch=m\
2 aster)](https://travis-ci.org/willmendesneto/build-checker)

Com isto terminamos a nossa integração com o servidor de integração contínua Travis-CI e
temos toda a nossa suite de testes rodando nos sistemas Linux e OSX. Nesta próxima etapa
vamos configurar as mesmas tarefas, mas para serem verificadas no sistema operacional Windows,
utilizando outro servidor de integração contínua chamado Appveyor.

Appveyor: checando seu código no Windows


Muitos projetos são desenvolvidos em sistemas operacionais baseados no Unix por padrão e
adicionar suporte ao Windows era considerado um grande um desafio para alguns, pois montar
um ambiente de teste do Windows não era algo trivial, exigindo a compra de licenças de software.
O serviço de integração contínua Appveyor54 é uma das soluç oes usadas para testes de projetos
hospedados no GitHub em ambientes Windows, facilitando este processo e assegurando que o nosso
código é cross-platform55 , funcionando nos principais sistemas operacionais.
Adicionar o suporte do Appveyor ao nosso projeto é uma tarefa bastante simples. Vamos então visitar
o site oficial do projeto e criar um login com as nossas informações.

Site do Appveyor

54
https://www.appveyor.com/
55
https://en.wikipedia.org/wiki/Cross-platform
Dando suporte ao seu código em vários sistemas operacionais 72

Na página de login temos algumas opções listadas com suporte a alguns dos principais repositórios
de código na internet. Nesta opção vamos utilizar o Github, para facilitar os próximos passos, mas
vale lembrar que você pode utilizar quaisquer das opções suportadas para login.

Efetuando login no Appveyor

Com o seu usuário criado e seu acesso funcionando, a página principal após o login é uma página
listando todos os seus repositórios baseado em uma categoria localizada no lado esquerdo do site. No
nosso caso, vamos escolher o projeto build-checker e clicaremos no botão “Add” para adicionarmos
o suporte ao nosso projeto.
Veremos então a página do nosso projeto com as informações específicas do mesmo, atualmente.

Agora que já temos o nosso projeto configurado vamos criar então o nosso arquivo appveyor.yml,
onde ficarão as nossas configurações de testes. Este arquivo é bem similar ao do Travis-CI em alguns
aspectos.
O conteúdo do nosso arquivo appveyor.yml com todas as alterações será o seguinte:
Adicionaremos a versão utilizada do NodeJS no campo environment do nosso arquivo de configu-
Dando suporte ao seu código em vários sistemas operacionais 73

ração.

1 ...
2 environment:
3 matrix:
4 - nodejs_version: "5.3.0"
5 ...

O campo platform será utilizado para descrever as plataformas utilizadas. Percebam que neste caso
ao invés de termos a diferenciação entre sistemas operacionais, temos entre plataformas do sistema
operacional (x86 e 64x).
Isso é interessante também caso haja a necessidade de ter uma noção da diferença de desempenho,
performance e outros aspectos da mesma aplicação em diferentes plataformas.

1 ...
2 platform:
3 - x86
4 - x64
5 ...

O campo install terá a lista dos nossos comandos iniciais. Perceba que o ps é um comando para
instalarmos o NodeJS com a especificada no arquivo. Após esta etapa limpamos o cache usando o
comando npm cache clean por medida de segurança, a fim de evitar possíveis falsos positivos nos
nossos testes e, terminado este comando, vamos então instalar as nossas dependências utilizando o
comando npm install.

1 ...
2 install:
3 - ps: Install-Product node $env:nodejs_version
4 - npm cache clean
5 - npm install
6 ...

O campo test_script terá a lista dos nossos comandos a serem executados no momento de execução
dos nossos testes. Estamos acessando diretamente a pasta node_modules e invocando os testes a
partir delas com o comando node_modules/.bin/istanbul cover node_modules/mocha/bin/_mo-
cha -- -R dot, pelo fato de utilizarmos o comando make test no nosso comando npm test.
Dando suporte ao seu código em vários sistemas operacionais 74

1 ...
2 test_script:
3 Run the test
4 - cmd: node_modules/.bin/istanbul cover node_modules/mocha/bin/_mocha -- -R dot

Como o nosso caso não é necessário a criação de um build, vamos adicionar a informação no nosso
arquivo com o valor off e vamos configurar o nosso build para ser finalizado o mais rápido possível
adicionando o campo fast_finish com o valor true.

1 build: off
2 matrix:
3 fast_finish: true
4 ...

O conteúdo final do nosso arquivo appveyor.yml com todas as alterações será o seguinte:

1 Fix line endings on Windows


2 init:
3 - git config --global core.autocrlf true
4 environment:
5 matrix:
6 - nodejs_version: "5.3.0"
7 platform:
8 - x86
9 - x64
10 install:
11 - ps: Install-Product node $env:nodejs_version
12 - npm cache clean
13 - npm install
14 test_script:
15 Run the test
16 - cmd: node_modules/.bin/istanbul cover node_modules/mocha/bin/_mocha -- -R dot
17 build: off
18 matrix:
19 fast_finish: true

Percebam que neste caso temos a lista dos nossos build diferenciada por plataformas na página de
listagem.
Dando suporte ao seu código em vários sistemas operacionais 75

Lista de plataformas utilizadas pelo serviço

Com a nova integração testada, vamos então atualizar o arquivo README.md do repositório com
o badge do Appveyor. Com isto você verá uma imagem com o status do build, assim como o que já
inserimos anteriormente.

1 [![Build Windows Status](https://ci.appveyor.com/api/projects/status/github/<nom\


2 e-do-seu-usuario-ou-organização>/<nome-do-seu-repositório>?svg=true)](https://ci\
3 .appveyor.com/project/<nome-do-seu-usuario-ou-organização>/<nome-do-seu-repositó\
4 rio>/branch/master)

Perceba que temos duas tags neste trecho de código. Substitua estas informações da seguinte forma:

• <nome-do-seu-usuario-ou-organização>: nome do seu usuário ou organização;


• <nome-do-seu-repositório>: nome do seu repositório;

Por exemplo, baseado no repositório de exemplo, o nosso badge terá o seguinte conteúdo.

1 [![Build Windows Status](https://ci.appveyor.com/api/projects/status/github/will\


2 mendesneto/build-checker?svg=true)](https://ci.appveyor.com/project/willmendesne\
3 to/build-checker/branch/master)

Como vocês puderam perceber adicionar suporte a vários sistemas operacionais e plataformas é uma
tarefa bastante simples com o Appveyor. Os próximos passos do livro serão mais voltados ao quesito
de melhorias na automação da checagem da nossa cobertura de código.
Dando suporte ao seu código em vários sistemas operacionais 76

Code coverage para o seu código


Finalizada a comunicação do nosso repositório com os serviços de integração contínua, vamos agora
adicionar novas ferramentas. Desta vez o foco é a cobertura de nosso código, checando se tudo está
sendo validado corretamente de maneira automatizada antes mesmo de continuarmos com as outras
etapas de desenvolvimento do nosso Nodebots.

Checando a cobertura de código do nosso projeto: Conhecendo o


Istanbul
O istanbul56 é um pacote NodeJS para verificar a cobertura de código no nosso repositório utilizando
vários parâmetros, como cobertura por linha de código, funções, declarações e engenharia reversa57 .
Vamos então adicionar este pacote ao nosso projeto utilizando o seguinte comando.

1 $ npm install --save-dev istanbul

Para verificarmos se ele está integrado no nosso repositório, basta digitarmos no nosso prompt/linha
de comando.

1 $ ./node_modules/.bin/istanbul help
56
http://gotwarlost.github.io/istanbul
57
https://pt.wikipedia.org/wiki/Engenharia_reversa
Dando suporte ao seu código em vários sistemas operacionais 77

Output do comando istanbul help

Como integramos anteriormente o MochaJS58 ao nosso repositório quando criamos os testes do


projeto, podemos simplesmente digitar o seguinte comando no nosso prompt/linha de comando.
58
https://mochajs.org/
Dando suporte ao seu código em vários sistemas operacionais 78

1 $ ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha

O retorno será o mesmo da imagem abaixo. Podem reparar que agora temos algumas novas
informações no rodapé das mensagens dos testes, tais com porcentagens de linhas, funções, branches
e declarações de métodos, classes ou objetos.
Dando suporte ao seu código em vários sistemas operacionais 79

output do comando mocha

Percebam que agora temos uma nova pasta chamada coverage com alguns arquivos e todas estas
informações listadas na nossa linha de comando. Utilizaremos elas nos próximos passos para a
integração com o serviço Coveralls.
Dando suporte ao seu código em vários sistemas operacionais 80

Integrando o servidor de integração contínua com o coveralls


Com as informações de cobertura de código coletadas, vamos então integrar um novo serviço
chamado coveralls59 . Ele será utilizado para integrar os dados de cobertura de código e deixar ele
visível, adicionando um badge no nosso README.md.

Site do serviço Coveralls

O login é bem simples e você terá que habilitar a integração com o seu Github. Após esta etapa
você verá uma lista com todos os seus repositórios cadastrados no Github. Clique no botão ao lado
esquerdo do seu repositório listado e espere a mensagem "Off" transformar-se em "On".
59
https://coveralls.io/
Dando suporte ao seu código em vários sistemas operacionais 81

Adicionando repositórios

Perceba que, com o repositório ativado, temos agora um link para a página detalhes. Ao clicarmos
neste link seremos direcionados para uma página com todas as informações iniciais para o setup
do projeto no coveralls. Para a nossa solução usaremos a opção de adicionarmos a informação do
coveralls no arquivo .coveralls.yml.

Vamos então copiar este conteúdo da opção do arquivo na página de setup e criaremos o novo
arquivo no nosso projeto. Dentro do nosso repositório local, vamos digitar o seguinte comando via
prompt/linha de comando.

1 $ touch .coveralls.yml

Vamos abrir este arquivo no nosso editor e adicionaremos o conteúdo dentro deste arquivo. Após
Dando suporte ao seu código em vários sistemas operacionais 82

esta etapa adicionaremos o pacote NodeJS à nossa lista de dependências de desenvolvimento para
integrarmos a infraestrutura do coveralls ao nosso projeto digitando o seguinte comando.

1 $ npm install --save-dev coveralls

Assim que enviarmos um novo código, podemos perceber que teremos as informações de porcen-
tagem de cobertura de código visível no site do coveralls, na área do nosso repositório. Com isto
podemos acompanhar todas as variações de cobertura de código, criamos validações e muito mais.

Depois disso podemos adicionar um novo badge com as informações do code coverage do nosso
projeto no arquivo README.md, contido no repositório do projeto. O padrão do badge é algo bem
simples:

1 [![Coverage Status](https://coveralls.io/repos/<nome-do-seu-usuario-ou-organizaç\
2 ão>/<nome-do-seu-repositório>/badge.svg?branch=master)](https://coveralls.io/r/<\
3 nome-do-seu-usuario-ou-organização>/<nome-do-seu-repositório>?branch=master)

Perceba que temos duas tags neste trecho de código. Substitua estas informações da seguinte forma:

• <nome-do-seu-usuario-ou-organização>: nome do seu usuário ou organização;


• <nome-do-seu-repositório>: nome do seu repositório;

Por exemplo, baseado no repositório de exemplo, o nosso badge terá o seguinte conteúdo.

1 [![Coverage Status](https://coveralls.io/repos/willmendesneto/build-checker/badg\
2 e.svg?branch=master)](https://coveralls.io/r/willmendesneto/build-checker?branch\
3 =master)

Após adicionarmos e salvarmos este código, o resultado final a ser renderizado será algo similar ao
da imagem a seguir.
Dando suporte ao seu código em vários sistemas operacionais 83

E com isto concluímos a nossa integração com o serviço do coveralls. Este é só um exemplo simples
de uma das várias funcionalidades deste serviço e recomendo fortemente que dêem uma lida na
documentação do coveralls60 para que vocês tenham uma maior sobre este serviço.

Verificando complexidade do código com o PlatoJS


PlatoJS é um pacote NodeJS que nos ajudará em algumas validações do nosso código nodebots. Ele
cria um relatório utilizando alguns dados gerados via análise estática do código do nosso projeto que
nos mostra algumas informações como complexidade do código, dificuldade de manutenção, linhas
de código, possiveis erros de implementação, dentre outros dados relevantes.

Caso queira saber mais sobre o PlatoJS, por favor acesse o repositório no Github do
projeto61

Sua instalação é muito fácil. Basta digitarmos o comando:

1 $ npm install --save-dev plato

E feito esta etapa, o plato foi instalado localmente como dependência de desenvolvimento na nossa
pasta node_modules do nosso projeto. Nosso próximo passo é adicionar um novo comando NPM.
Agora nós teremos o comando code-analysis que vai acionar o plato ao nosso projeto.

60
https://coveralls.zendesk.com/hc/en-us
61
https://github.com/es-analysis/plato
Dando suporte ao seu código em vários sistemas operacionais 84

1 {
2 ...
3 "scripts": {
4 "start": "nodemon ./src/index.js -e js,json --watch ./src",
5 "test": "make test",
6 "code-analysis": "plato -r -d report src test"
7 },
8 ...
9 }

E para acionarmos o PlatoJS, basta digitarmos:

1 $ npm run code-analysis

E após este comando será criada uma pasta de nome report com as informações da análise do nosso
repositório.

Dentro desta pasta teremos vários arquivos com as informações retornadas da análise do PlatoJS
que podemos visualizar mais detalhes acessando o arquivo index.html no nosso navegador.
Esta página terá informações de cada arquivo e gráficos mostrando dados como nível de complexi-
dade e linhas de código, como podemos ver na figura abaixo.
Dando suporte ao seu código em vários sistemas operacionais 85

Página de report com as informações extraídas pelo PlatoJS


Apêndice
Protoboard
Quando você precisa rapidamente e temporariamente protótipo de um circuito, você estará usando
um protoboard. Protoboard são placas que conseguem simular conexões de componentes eletrônicos.
Ela possui pequenos oríficios, aonde conectaremos os nossos fios

Protoboard

Piezo
Piezo é um sensor que emite um sinal sonoro, tal como uma buzina. Este sinal pode ser criado em
uma aplicação Nodebots a partir de um valor numérico ou uma abstração de notas sonoras, o que
torna a sua manipulação mais simples.
Apêndice 87

Piezo

Resistores
Os resistores são vastamente utilizados na eletrônica, send um dos primeiros componentes eletrôni-
cos com o qual lidamos e um dos que mais utilizaremos. Eles são as bastante pequenos em forma de
pílula com listras na maioria das placas de circuito.
Um resistor é um componente eletrônico que limita o fluxo de elétrons dissipando energia sob a
Apêndice 88

forma de calor, pois a eletricidade tem que lutar para fluir através de algo com uma alta resistência.
Utilizando assim uma grande quantidade de energia e convertendo isso em calor.

Resistores

LED (Light-emitting diode)


LED é uma abreviação de diodo emissor de luz (Light-emitting diode). Um componente do tipo de
diodo que emite luz.
Apêndice 89

Light-emitting diode

Sensores
Dispositivo que converte dados do mundo real (analógica) em dados que um computador pode
entender usando ADC e convertendo os dados do formato Analógico para o Digital. Utilizaremos
sensores para detectarmos eventos ou mudanças no local e enviaremos para leitura na nossa
aplicação.

Condutor de Proteção (Fio Terra ou Ground)

É o fio de entrada de condutor elétrico que possui a função de “aterrar” todos os dispositivos que
precisarem utilizar seu potencial como referência ou suas propriedades elétricas.
Em sistemas de potência, o terra possui as funções de referência elétrica para a tensão, sistemas de
proteção, controle para excesso de carga/energia e proteção de equipamentos.
Apêndice 90

Fios de condução
Fios isolados curto com extremidades sem capa de isolamento que usamos para conectar dois pontos
em um circuito ou protoboard.

Fios de condução

Botão

É um mecanismo simples para controlar alguns aspectos de uma máquina ou um processo. Ao clicar
no botão a corrente é liberada e ao soltar o botão a corrente é cancelada.

Botão
Próximos passos
Finalizando agora o conteúdo deste livro com alguns exemplos bastante didáticos já vemos o poder
do Javascript aliado a robótica em nossas aplicações. Estes são exemplos que podem ser incorporados
ao nosso cotidiano e ensinamentos para a execução de várias outras idéias que surgirão.
Espero que tenham gostado do conteúdo deste livro e dos exemplos tanto quanto eu tive o prazer
de compartilhar este conteúdo. Caso tenha dúvidas, questões ou até mesmo uma conversa, entre em
contato pelo email willmendesneto@gmail.com.
Muito obrigado!

Vous aimerez peut-être aussi