50
Universidade Estadual de Maringá Centro de Tecnologia – Departamento de Informática Especialização em Desenvolvimento de Sistemas para Web Buster.js, um framework de testes para JavaScript Obedi de Paula Ferreira Prof. Me. Gécen de Marchi Orientador Maringá, 2013

Buster.js, um framework de testes para JavaScript de Paula Ferreira... · automated tests for JavaScript is not a simple task as in other modern programming languages, primarily because

  • Upload
    buidat

  • View
    213

  • Download
    0

Embed Size (px)

Citation preview

Universidade Estadual de Maringá Centro de Tecnologia – Departamento de Informática Especialização em Desenvolvimento de Sistemas para Web

Buster.js, um framework de testes para JavaScript

Obedi de Paula Ferreira

Prof. Me. Gécen de Marchi

Orientador

Maringá, 2013

Universidade Estadual de Maringá Centro de Tecnologia – Departamento de Informática Especialização em Desenvolvimento de Sistemas para Web

Obedi de Paula Ferreira

Buster.js, um framework de testes para JavaScript

Trabalho submetido à Universidade Estadual de Maringá como requisito para obtenção do título de Especialista em

Desenvolvimento de Sistemas para Web.

Orientador: Prof. Me. Gécen de Marchi

Maringá, 2013

Universidade Estadual de Maringá Centro de Tecnologia – Departamento de Informática Especialização em Desenvolvimento de Sistemas para Web

Obedi de Paula Ferreira

Buster.js, um framework de testes para JavaScript

Maringá, 12 de Abril de 2013.

Prof. Flávio Uber Ass.:________________________

Prof. Munif Gebara Junior Ass.:________________________

Prof. Dr. Gécen de Marchi (Orientador) Ass.:________________________

RESUMO

Um dos principais desafios da Engenharia de Software é lidar com o aumento da complexidade do software e a diminuição dos prazos de entrega, dado que esta relação compromete diretamente a qualidade do software. No intuito de minimizar o impacto deste e de outros desafios, foram criados ao longo dos anos diferentes padrões e metodologias de desenvolvimento que pudessem proporcionar ganho de produtividade e garantir maior qualidade do software. Dentre essas metodologias, um conjunto conhecido como “metodologias ágeis” tem se destacado nos últimos anos. Uma destas metodologias ágeis, o Extreme Programming, ou XP, introduziu uma prática popularmente conhecida como Test-Driven Development, ou TDD, que consiste em implementar testes automatizados que irão guiar o desenvolvimento de cada funcionalidade do software. Escrever testes automatizados para JavaScript não é uma tarefa tão simples quanto nas demais linguagens de programação modernas, basicamente pelo fato de que existem uma série de fatores externos à linguagem que em alguns casos dificultam com que código JavaScript seja testado de forma apropriada. Dentre estes fatores está o fato de JavaScript depender diretamente do HTML, do CSS, e de como cada navegador renderiza estes elementos. Apesar de existirem diversos frameworks de testes para JavaScript disponíveis no mercado, a maioria deles ainda não alcançou o nível de maturidade necessário para se difundir como referência nesse domínio, assim como por exemplo, o JUnit é referência para testes automatizados para a linguagem Java. Sendo assim, após entender as peculiaridades pertinentes aos ambientes onde esse código é executado (os navegadores) e conhecer o estado atual dos frameworks de testes automatizados para JavaScript, encontrou-se o Buster.JS, um framework que apesar de encontrar-se em fase beta, se mostrou bastante robusto e completo, com funcionalidades que ajudam o desenvolvedor a focar seus esforços mais na qualidade dos testes e não na infraestrutura dos testes para diferentes navegadores. O Buster.JS possui instalação e configuração simples, e linguagem similar a frameworks de outras linguagens populares do mercado. Ele utiliza uma técnica baseada no conceito mestre x escravo para permitir que os testes sejam executados ao mesmo tempo em diversos navegadores diferentes, nas versões e plataformas escolhidas pelo desenvolvedor.

Palavras-chave: JavaScript, Testes Unitários, Desenvolvimento Guiado por Testes, Testes Automatizados, Buster.JS.

ABSTRACT

One of the main challenges of Software Engineering in is how to handle the growth of software complexity and the deadline shortenings, given that such relation jeopardizes directly the quality of the software. In an attempt of minimizing the impact of these and other challenges, a variety of patterns and software development methodologies were created throughout the years, that could help improve productivity and assure better quality of the software. Among these methodologies, the so called “agile methodologies” stood out in recent years. Among these methodologies, the Extreme Programming introduced a practice popularly known as TDD (Test-Driven Development), which consists in implementing automated tests that will guide the development of each functionality of the software. Writing automated tests for JavaScript is not a simple task as in other modern programming languages, primarily because there are a lot of factors external to language that in some cases make testing JavaScript code properly more difficult. Among these factors is the fact that JavaScript depends heavily on HTML, CSS, and how each browser renders these elements. Although there are many JavaScript frameworks available, most of them have not yet reached the necessary level of maturity to stand as an absolute reference in this domain, just like JUnit is a reference to automated tests for the Java programming language, for example. Therefore, after understanding the peculiarities relevant to the environments where this code is executed (the browsers) and after knowing the state of art of JavaScript testing frameworks, Buster.JS was highlighted. Buster.JS is tool that despite being beta, was found to be robust and complete, with features that can help developers to focus on the tests quality over the tests infrastructure for different browsers. Buster.JS installation and configuration is simple, and its language is quite similar to other popular frameworks from other languages. It uses a master x slave technique to allow tests to be executed simultaneously in different browsers, in the versions and platforms chosen by the developer.

Keywords: JavaScript, Unit Tests, Test-Driven Development, Automated Tests, Buster.JS.

LISTA DE ILUSTRAÇÕES

Figura 1 - Exemplo de documento HTML ............................................................................... 18

Figura 2 - Árvore DOM da figura anterior ............................................................................... 19

Figura 3 - Exemplo de um “assert equals” no framework JUnit, para Java ............................. 24

Figura 4 - Exemplo de caso de teste em Buster.js .................................................................... 28

Figura 5 – Calculadora.js: Classe Calculadora, em JavaScript ................................................ 29

Figure 6 – Calculadora-test.js: Teste da class Calculadora, em JavaScript .............................. 29

Figure 7 - Exemplo de estrutura de um projeto ........................................................................ 30

Figura 8 – Arquivo de configuração do Buster.js ..................................................................... 30

Figura 9 - Organização das classes e testes .............................................................................. 31

Figura 10 - Arquivo buster.html ............................................................................................... 32

Figura 11 - Relatório de execução dos testes ............................................................................ 32

Figura 12 - Resultado de execução com falha .......................................................................... 33

Figure 13 - Comando buster static ............................................................................................ 34

Figura 14 - Estrutura do exemplo validação de formulário ...................................................... 35

Figura 15 - Conteúdo do arquivo test/fixtures/formulario.html ............................................... 35

Figura 16 - Configuração do Buster.js para uso de fixtures ..................................................... 36

Figura 17 - Conteúdo da classe ValidaForm.js ......................................................................... 37

Figura 18 - Formulário de login do arquivo test/fixtures/formulario.html ............................... 37

Figura 19 - Formulário de login apresentando erro .................................................................. 38

Figura 20 - Clase de teste ValidaForm-test.js ........................................................................... 38

Figura 21 - Comando buster server .......................................................................................... 39

Figura 22 - Página de captura de slaves ................................................................................... 39

Figura 23 - Relatório da execução dos testes ........................................................................... 40

Figura 24 - Tentativa de execução dos testes sem slaves capturados ....................................... 40

Figura 25 - Relatório de execução com falhas ......................................................................... 40

Figura 26 - Configuração do Buster.js no modo node .............................................................. 41

Figura 27 - Classe Calculadora.js alterada para o modo node.................................................. 42

Figura 28 Classe Calculadora-test.js alterada para o modo node ............................................. 42

SUMÁRIO

CAPÍTULO 1. INTRODUÇÃO ................................................................................................................... 8

1.1. PROBLEMA ...................................................................................................................................................... 9

1.2. JUSTIFICATIVA ............................................................................................................................................ 10

1.3. MOTIVAÇÃO ................................................................................................................................................. 10

1.4. OBJETIVO GERAL ....................................................................................................................................... 10

1.5. OBJETIVOS ESCPECÍFICOS ..................................................................................................................... 11

1.6. LIMITAÇÕES DA PESQUISA .................................................................................................................... 11

1.7. ESTRUTURA DO TRABALHO ................................................................................................................. 11

CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA .......................................................................................... 13

2.1. DESENVOLVIMENTO ÁGIL ..................................................................................................................... 13

2.1.1. O MANIFESTO ÁGIL ................................................................................................................................. 13

2.2. EXTREME PROGRAMMING (XP) .......................................................................................................... 14

2.3. TEST-DRIVEN DEVELOPMENT ............................................................................................................. 16

2.3.1. CICLO DE DESENVOLVIMENTO COM TDD ............................................................................................ 17

2.4. A LINGUAGEM JAVASCRIPT .................................................................................................................... 17

2.4.1. ECMASCRIPT, O NÚCLEO DA LINGUAGEM JAVASCRIPT .................................................................... 17

2.4.2. DOM ........................................................................................................................................................... 18

2.4.3. BOM ........................................................................................................................................................... 19

2.4.4. CARACTERÍSTICAS DA LINGUAGEM ....................................................................................................... 19

2.4.5. TESTES UNITÁRIOS EM JAVASCRIPT ..................................................................................................... 20

CAPÍTULO 3. FRAMEWORKS DE TESTES PARA JAVASCRIPT .................................................... 22

3.1. XUNIT TEST FRAMEWORKS .................................................................................................................. 22

3.1.1. BDD ............................................................................................................................................................ 23

3.2. ESTRUTURA BÁSICA DE UM FRAMEWORK DE TESTES ............................................................. 23

3.2.1. CASO DE TESTE, OU CENÁRIO DE TESTE .............................................................................................. 23

3.2.1.1. ASSERÇÕES ................................................................................................................................................. 24

3.2.2. FIXTURES ................................................................................................................................................... 24

3.2.3. SUÍTE DE TESTES ...................................................................................................................................... 25

3.3. TEST RUNNERS ........................................................................................................................................... 25

3.3.1. RUNNERS “IN-BROWSER” ...................................................................................................................... 26

3.3.2. RUNNERS HEADLESS ............................................................................................................................... 26

CAPÍTULO 4. BUSTER.JS ........................................................................................................................ 28

4.1. INSTALAÇÃO E CONFIGURAÇÃO .......................................................................................................... 29

4.2. MODO IN-BROWSER ................................................................................................................................. 31

4.2.1. MODO HTML ........................................................................................................................................... 31

4.2.2. MODO ESTÁTICO ....................................................................................................................................... 33

4.2.3. MODO SERVIDOR ...................................................................................................................................... 34

4.3. MODO HEADLESS ...................................................................................................................................... 41

4.4. MODO NODE ................................................................................................................................................ 41

4.5. ASSERÇÕES ................................................................................................................................................... 42

4.5.1. ASSERT.SAME() ......................................................................................................................................... 43

4.5.2. ASSERT.EQUALS() ..................................................................................................................................... 43

4.5.3. ASSERT.GREATER() .................................................................................................................................. 43

4.5.4. ASSERT.LESS() ........................................................................................................................................... 43

4.5.5. ASSERT.DEFINED() ................................................................................................................................... 43

4.5.6. ASSERT.ISNULL() ...................................................................................................................................... 43

4.5.7. ASSERT.ISOBJECT() .................................................................................................................................. 43

4.5.8. ASSERT.ISFUNCTION() ............................................................................................................................. 44

4.5.9. ASSERT.ISTRUE() ...................................................................................................................................... 44

4.5.10. ASSERT.ISFALSE() ..................................................................................................................................... 44

4.5.11. ASSERT.ISSTRING() .................................................................................................................................. 44

4.5.12. ASSERT.ISBOOLEAN() .............................................................................................................................. 44

4.5.13. ASSERT.ISNUMBER() ............................................................................................................................... 44

4.5.14. ASSERT.ISNAN() ....................................................................................................................................... 45

4.5.15. ASSERT.ISARRAY() ................................................................................................................................... 45

4.5.16. ASSERT.EXCEPTION() ............................................................................................................................... 45

4.5.17. ASSERT.CONTAINS() ................................................................................................................................. 45

4.5.18. ASSERT.TAGNAME() ................................................................................................................................. 45

4.5.19. ASSERT.CLASSNAME() ............................................................................................................................. 45

CAPÍTULO 5. CONCLUSÃO .................................................................................................................... 47

REFERÊNCIAS .................................................................................................................................................. 48

8

CAPÍTULO 1. INTRODUÇÃO

Uma das questões mais complexas no que diz respeito ao processo de desenvolvimento de

software, é como garantir a qualidade do software (FEITOSA, 2007)

Dos fatores que afetam a qualidade do software, alguns podem ser medidos diretamente,

como por exemplo a relação quantidade de falhas por linhas de código, enquanto que outros

apenas podem ser medidos indiretamente, como a usabilidade, por exemplo. São estes fatores

que calculados, servem de indicadores da qualidade do software (PRESSMAN, 2005, apud

FEITOSA, 2007).

SOMMERVILLE (2003, apud FEITOSA, 2007) afirma que um dos principais desafios da

Engenharia de Software neste século é lidar com o aumento da complexidade do software e a

diminuição dos prazos de entrega, dado que esta relação compromete diretamente a qualidade

do software.

No intuito de minimizar o impacto deste e de outros desafios, foram criados ao longo dos

anos diferentes padrões e metodologias de desenvolvimento de software que pudessem

proporcionar ganho de produtividade e garantir maior qualidade do software.

Dentre essas metodologias, um conjunto conhecido como “metodologias ágeis” vem se

mostrando úteis na busca de software de melhor qualidade, produzidos em menos tempo e de

uma forma mais econômica que o habitual. Em geral essas metodologias são regidas por um

conciso conjunto de valores, princípios e práticas, que diferem substancialmente da forma

tradicional de se desenvolver software (TELES, 2008).

No desenvolvimento de software, CIFANI (2011) cita como outro grande problema a

manutenção do software em produção, pois a necessidade de mudança no software pode surgir

a todo instante e uma pequena mudança em uma pequena parte do software pode ter impacto

nas demais partes do mesmo. Torna-se então necessário uma forma de desenvolvimento que

possa de alguma forma dar ao desenvolvedor a visibilidade do impacto de uma mudança no

software.

9

O Desenvolvimento Guiado por Testes, ou TDD (do ingles, Test-Driven Development), é uma

das práticas do XP que tem por objetivo antecipar a identificação e correção de falhas durante

o desenvolvimento (TELES, 2008).

Esta prática consiste em implementar testes automatizados para cada funcionalidade do

software antes mesmo de implementá-la (FEITOSA, 2007). Dessa forma, após a

implementação da funcionalidade propriamente dita, o desenvolvedor pode executar os testes

automatizados e verificar se o código implementado está correto, pois se o teste passar, o

desenvolvedor saberá que o código foi bem implementado (FEITOSA, 2007).

De forma semelhante, após uma mudança em qualquer parte do software, o desenvolvedor

saberá o impacto daquela mudança nas demais partes do sistema, verificando quantos testes

não passaram.

1.1. PROBLEMA

Atualmente as linguagens de desenvolvimento mais modernas e populares, como o Ruby, o

Java, e muitas outras, possuem uma enorme gama de frameworks diversas ferramentas que

dão suporte ao uso de testes automatizados.

No entanto, apesar de a linguagem JavaScript estar massivamente presente no

desenvolvimento web, existem muitos elementos a serem considerados e que dificultam os

testes de JavaScript de forma apropriada. Um destes elementos é a interdependência entre

HTML e CSS.

Pode-se dizer que o problema em se testar JavaScript se resume aos navegadores. Existem

diversos navegadores disponíveis, e cada um com suas peculiaridades. Linguagens de

desenvolvimento tradicionalmente excelentes para testes unitários, geralmente tem seus testes

rodando em um ambiente padronizado, previsível e estável, onde os efeitos de certas ações

são facilmente compreensíveis (ZAKAS, 2009).

Atualmente existem disponíveis diversos frameworks que dão suporte a testes automatizados

em JavaScript, porém a maioria delas ainda não alcançou o nível de maturidade necessário

para se difundir como referência nesse domínio, assim como por exemplo, o JUnit é

referência para testes automatizados para a linguagem Java.

10

1.2. JUSTIFICATIVA

NIST (2002, apud TELES, 2008) estima que falhas de software causem um prejuízo anual

aproximado de mais de 60 bilhões de dólares para a economia americana. Um terço destes

custos poderia ser eliminado se fossem utilizadas infraestruturas de testes mais adequadas,

que permitissem identificar e resolver falhas mais cedo e de forma mais eficaz.

Apesar desta realidade, grande parte dos desenvolvedores de software hoje, utilizam-se da

premissa de que as bibliotecas JavaScript por eles usadas já foram previamente testadas por

seus mantenedores, e com isso ignoram a importância de se ter testes que garantam o

comportamento esperado dessas bibliotecas em suas aplicações.

Aliado a este fato, o desconhecimento de frameworks de testes maduros contribuem para tal

negligência em se testar código JavaScript.

Sendo assim, para se caminhar em direção a uma cobertura de testes satisfatória no código

JavaScript de uma aplicação, é importante entender as peculiaridades pertinentes aos

ambientes onde esse código é executado (os navegadores) e conhecer o estado atual dos

frameworks de testes automatizados para que se optar pelo mais indicado.

1.3. MOTIVAÇÃO

O uso de testes automatizados é um domínio no qual muito tem se falado ao longo dos anos.

A maioria das linguagens modernas possuem ferramentas robustas e maduras para suporte a

testes, e em alguns casos essas linguagens já até possuem métodos nativos para teste, como é

o caso da linguagem Ruby.

No entanto, pouca importância tem-se dado aos testes de código JavaScript, e pouco se

conhece sobre as ferramentas de teste existentes.

1.4. OBJETIVO GERAL

Propor o uso de um dos frameworks de testes unitários disponíveis para JavaScript, que dê

suporte robusto à construção de testes unitários, permitindo que desenvolvedores deem maior

11

atenção à construção de testes de qualidade, e não investir tempo demasiado na criação e

manutenção de infraestrutura necessária para a execução dos testes em diferentes ambientes.

1.5. OBJETIVOS ESCPECÍFICOS

Os objetivos específicos para se alcançar o objetivo geral traçado são:

i. Estudar testes de unidade para compreender seu papel dentro do domínio dos testes de

software;

ii. Identificar e estudar outros níveis de testes automatizados de software;

iii. Estudar frameworks de testes disponíveis para JavaScript;

iv. Escolher um dos frameworks estudados para um aprendizado mais aprofundado;

v. Implementar uma suíte de testes utilizando o framework escolhido para colocar em

prática os conceitos estudados.

1.6. LIMITAÇÕES DA PESQUISA

Este trabalho se limita a estudar os pontos fortes e fracos do framework a ser escolhido, sem a

preocupação de sugerir melhorias ou tentar encontrar formas de mitigar as limitações da

ferramenta.

Quanto a abordagem e profundidade dos testes, serão abordados apenas testes unitários de

software.

1.7. ESTRUTURA DO TRABALHO

Este trabalho é dividido em cinco capítulos, conforme descrito abaixo:

O Capítulo 2, Revisão Bibliográfica, apresenta a base teórica sobre Testes de Software,

Desenvolvimento Ágil e Desenvolvimento Guiado por Testes. São também apresentados os

fundamentos básicos da linguagem JavaScript.

O Capítulo 3, Metodologia de Desenvolvimento, relata as etapas da metodologia de pesquisa

utilizada.

12

No Capítulo 4, Frameworks de TDD para JavaScript, são apresentados alguns dos

frameworks mais populares do mercado atualmente e são levantados os aspectos desejáveis a

um framework ideal para este fim. Ao final é apresentada uma análise detalhada do framework

que melhor satisfaz estes critérios.

Finalizando o trabalho, o Capítulo 5, Conclusão, apresenta a conclusão geral do trabalho, bem

como dificuldades, contribuições e possíveis trabalhos futuros.

13

CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA

2.1. DESENVOLVIMENTO ÁGIL

Ao longo dos anos, alguns desenvolvedores acreditaram que os modelos tradicionais de

desenvolvimento, como o modelo cascata, estavam errados, pois suas práticas burocráticas

não se encaixavam na realidade dinâmica do mercado de software (CIFANI, 2011). O maior

problema destas metodologias era o fato de existirem muitas etapas vitais e sequenciais, ao

ponto de que se uma etapa atrasasse, todo o projeto sofreria impacto direto deste atraso

(CIFANI, 2011).

É neste contexto que com o tempo algumas metodologias foram surgindo com o intuito de

minimizar estes problemas, priorizando interações mais constantes com os clientes e entregas

de subprodutos em períodos mais curtos de tempo.

Apesar de muitas dessas metodologias existirem há anos, o termo desenvolvimento ágil

ganhou força após a criação do Manifesto Ágil, um conjunto de princípios a serem seguidos

para se obter sucesso no desenvolvimento de software frente as necessidades do mundo atual.

2.1.1. O Manifesto Ágil

O Manifesto Ágil (BECK et al, 2001) foi criado em Fevereiro de 2001 e surgiu a partir de um

encontro de 17 profissionais veteranos no desenvolvimento de software que queriam discutir

formas de melhorar o desempenho de seus projetos de software.

Cada um dos envolvidos na criação do Manifesto Ágil possuía suas próprias práticas e teorias

particulares sobre a melhor forma se desenvolver software, porém havia um consenso sobre

um pequeno conjunto de 12 princípios que havia sido respeitado em todos os projetos de

sucesso (TELES, 2008). Estes princípios se resumem nos seguintes valores descritos no

manifesto (BECK et al, 2011):

i. Indivíduos e interações mais que processos e ferramentas

14

ii. Software em funcionamento mais que documentação abrangente

iii. Colaboração com o cliente mais que negociação de contratos

iv. Responder a mudanças mais que seguir um plano

“Ou seja, mesmo havendo valor nos itens à direita, valorizamos mais os itens à esquerda”

(BECK et al, 2011).

Sendo assim, o termo Desenvolvimento Ágil hoje descreve qualquer abordagem de

desenvolvimento que siga estes princípios (TELES, 2008).

2.2. EXTREME PROGRAMMING (XP)

O Extreme Programming, ou XP, como é comumente conhecida, é uma das metodologias de

desenvolvimento ágil mais populares atualmente. Sua origem vem de algumas práticas do

desenvolvimento em Smalltalk, disseminadas principalmente por Kent Beck e Ward

Cunningham em meados da década de 80. Dentre essas práticas, pode-se citar a programação

em par, refatoração (melhoria no código), feedback constante do cliente e testes

automatizados (TELES, 2005).

Em projetos XP, o software é desenvolvido em um modo interativo e incremental, onde a cada

semana os desenvolvedores se reúnem com o cliente para priorizar um conjunto fechado de

funcionalidades que possam ser implementadas completamente no ciclo de uma semana

(TELES, 2008).

Após esse período, o cliente tem a oportunidade de utilizar e avaliar o que foi produzido e

com base nessas experiências, sugerir melhorias e mudanças (TELES, 2008). Nesse ponto, o

desenvolvimento guiado por testes torna-se essencial para que os desenvolvedores possam

realizar mudanças com segurança, visto que os testes indicarão eventuais falhas decorrentes

da alteração. Os testes podem também ajudar a garantir que o código desenvolvido atenda as

necessidades levantadas pelo cliente (FEITOSA, 2007).

Quatro valores regem o XP. São eles:

i. Comunicação: XP mantém a comunicação fluente através do uso de práticas

que não podem ser feitas sem se comunicar, tais como testes unitários, programação

em par e estimativa de tarefas (BECK e ANDRES, 2004). O efeito dessas simples

15

práticas é que desenvolvedores e gerentes tem que se comunicar sobre o que realmente

é importante no projeto.

ii. Simplicidade: Segundo BECK e ANDRES (2004), XP parte do princípio de

que é melhor fazer algo simples no primeiro momento e caso necessário, investir

tempo para que este algo seja alterado, do que fazer algo mais complexo e que talvez

nunca venha a ser usado. Em outras palavras, os desenvolvedores devem codificar

primeiro tudo que é claramente importante para o software, evitando desenvolver o

que não é essencial (FEITOSA, 2007). Simplicidade e Comunicação possuem uma

relação de suporte mútuo, pois quanto melhor a comunicação, mais claro é aos

desenvolvedores o que exatamente precisa ser feito. E quanto mais simples o software,

menos o desenvolvedor precisa comunicar sobre seu comportamento, o que tende a

levar a uma comunicação mais completa (BECK e ANDRES, 2007).

iii. Feedback: No desenvolvimento de software quanto mais cedo uma falha é

detectada, mais barata é sua correção (FEITOSA, 2007). Portanto, o XP é organizado

em ciclos curtos de feedback do cliente, onde o objetivo é apresentar uma nova

funcionalidade ao usuário, de modo que ele possa o mais breve possível detectar

eventuais falhas (TELES, 2005). Feedback por sua vez contribui diretamente com

Comunicação e Simplicidade, pois quanto mais feedbacks, mais fácil de se comunicar

(BECK e ANDRES, 2007).

iv. Coragem: Desenvolvedores XP partem do princípio de que problemas irão

ocorrer, inclusive aqueles mais temidos, e ter coragem em XP significa ter confiança

nos mecanismos de segurança utilizados para proteger o projeto, mecanismos estes

que ajudarão a reduzir ou eliminar as consequências destes problemas (TELES, 2005).

Entretanto, valores são abstratos demais para guiar comportamentos. Eles indicam o

propósito, algo em que se acredita, a razão pela qual age-se de determinada forma (TELES,

2008). Práticas, por sua vez, são aquilo que efetivamente se faz no dia a dia, guiando-se por

um conjunto de valores e princípios. Identificá-las é útil pois são claras e objetivas (TELES,

2008).

O XP possui uma série de práticas absolutas que servem como um portfólio de ações a

disposição de uma equipe ágil de desenvolvimento. Segundo TELES (2008), aplicar essas

práticas ou não, é uma escolha diretamente dependente da situação, do contexto. Se dada

situação muda, seleciona-se práticas diferentes para abordar estas condições de mudança.

16

TELES (2008) também sugere que as práticas do XP sejam experimentadas como uma

hipótese, onde aplica-se determinada prática, verificando-se o quanto esta prática ajuda no

resultado do projeto. TELES (2008) complementa que embora esse conjunto de práticas tenha

sido concebido para funcionar bem em conjunto, sua adoção uma a uma é o suficiente para se

perceber benefícios. A medida que mais práticas passam a ser utilizadas em conjunto, mais

nítidas são as melhorias.

As práticas do XP são divididas entre: (i) primárias e (ii) corolárias (TELES, 2008). As

práticas primárias são aquelas que podem gerar benefício imediato independente de qualquer

outra que esteja-se utilizando. Já as corolárias tendem a ser mais difíceis de dominar sem que

antes tenha-se colocado em uso as práticas primárias.

2.3. TEST-DRIVEN DEVELOPMENT

O Desenvolvimento Guiado por Testes é uma das práticas primárias do XP e é nela que este

trabalho irá se focar. Seu princípio básico é a inversão da sequência tradicional de: (i) projetar,

(ii) implementar e enfim (iii) testar (SANTOS, 2010). Na prática, o desenvolvimento com

TDD é constituído de pequenas iterações onde casos de testes são escritos contemplando uma

nova funcionalidade, e depois, o código necessário para passar esses casos de teste é

implementado. Diante de eventuais mudanças, basta que o software seja software seja

refatorado de forma que os testes continuem passando (FEITOSA, 2007).

Cada iteração possui um micro objetivo, que terá sido alcançado quando os testes escritos

antes da implementação passarem. Dessa forma, o escopo da tarefa na qual o desenvolvedor

deverá se focar fica reduzido, ao passo que se dedicará ao seu micro objetivo ao invés de se

preocupar com todo software (FEITOSA, 2007), forçando o desenvolvimento a acontecer de

forma e incremental (SANTOS, 2010).

“TDD não é sobre testes, é sobre como usar testes para criar software de maneira simples e incremental. Além de melhorar a qualidade e o projeto do software, ele também simplifica o processo de desenvolvimento” (GOLD et al., 2004, apud SANTOS, 2010)

Tal simplificação do processo de desenvolvimento, contribui para um aumento no nível de

confiança do desenvolvedor no produto sendo construído SANTOS (2010).

17

2.3.1. Ciclo de desenvolvimento com TDD

Como citado anteriormente nesta seção, o desenvolvimento com TDD é constituído de

iterações. Kent Beck (BECK, 2003) resume o ritmo de desenvolvimento com TDD em 5

passos:

i. Escrever um teste novo;

ii. Rodar todos os testes e ver que o novo teste falha;

iii. Implementar o código necessário e suficiente para que o teste passe;

iv. Rodar todos os testes e ver que todos passam;

v. Refatorar o código implementado para remover duplicações.

2.4. A LINGUAGEM JAVASCRIPT

Desenvolvida pela Netscape Communications em 1994, o JavaScript é uma linguagem de

script massivamente utilizada para dar mais dinamismo à páginas da web. Ela permite ao

desenvolvedor manipular os elementos de uma página (HTML e CSS) para criar efeitos e

aplicar conteúdo dinâmico.

A linguagem de marcação HTML é utilizada para ajustar o conteúdo e a formatação de uma

página na web, a linguagem de estilo CSS para estilizar como este conteúdo deve ser exibido,

e o JavaScript é utilizado para criar efeitos e aplicações RIA (do inglês, Rich Internet

Application, ou Aplicações de Internet Rica). Na prática, o JavaScript possui métodos que

permitem aos desenvolvedores interagir com o HTML e o CSS para criar tais aplicações

(MOZILLA, 2012). O JavaScript é constituído por três elementos, o ECMAScript, o DOM

(do inglês, Document Object Model) e o BOM (do inglês, Browser Object Model).

2.4.1. ECMAScript, o núcleo da linguagem JavaScript

O núcleo da linguagem JavaScript é padronizado pelo comitê ECMA TC-39 sob o nome de

ECMAScript (MOZILLA, 2012). É este padrão que define a sintaxe do JavaScript, suas

palavras chaves, palavras reservadas, o controle de fluxo, tipos de dados, objetos, operadores,

18

dentre outros elementos. Atualmente o ECMAScript está em sua verão 5, porém a maioria dos

navegadores modernos implementam a versão 3, com algumas partes apenas da versão 5

(MOZILLA, 2012).

Além do JavaScript, outras linguagens também utilizam o ECMAScript como base, como o

Action Script, por exemplo.

2.4.2. DOM

O DOM é uma API (do inglês, Application Programming Interface) para manipulação de

documentos XML e que foi estendida para documentos HTML. Essa API mapeia os

documentos em uma hierarquia de nós, conhecida como DOM Tree, ou Árvore DOM, onde

cada parte de um documento é representado por um tipo de nó contendo um tipo diferente de

dado (ZAKAS, 2009).

Figura 1 - Exemplo de documento HTML

19

Figura 2 - Árvore DOM da figura anterior

A API do DOM permite aos desenvolvedores manipular toda a árvore, permitindo que

qualquer nó seja facilmente removido, adicionado ou modificado.

2.4.3. BOM

O BOM (do inglês, Browser Object Model) provê uma API que expõe elementos do

navegador que estão fora do contexto da página sendo exibida. Segundo ZAKAS (2009), esta

é a única parte da implementação do JavaScript que não é regulamentada, e devido a isso,

cada navegador possui sua própria implementação, o que o torna potencialmente

problemático.

Basicamente, o BOM engloba as janelas e os frames do navegador, vem dele a capacidade de

abrir janelas pop up, a capacidade de mover, redimensionar e fechar janelas, o suporte a

cookies e etc. Além disso, ele disponibiliza alguns objetos que proveem informações

detalhadas sobre o navegador, sobre a página sendo exibida e sobre a tela do usuário

(ZAKAS, 2009).

2.4.4. Características da linguagem

Como principais características da linguagem JavaScript, pode-se citar:

20

i. É imperativa e estruturada

ii. Possui tipagem fraca e dinâmica

iii. É baseada em objetos (ou baseada em protótipos)

iv. É interpretada, não compilada

v. Permite funções aninhadas

vi. Permite funções anônimas

vii. Possui suporte a expressões regulares

2.4.5. Testes unitários em JavaScript

Segundo Wells (1999), testes unitários são um dos alicerces do Extreme Programming. Em

um projeto de software, todas as classes do sistema deveriam ser testadas no nível unitário, e

qualquer código sem tal cobertura mínima de testes não deveria ser considerado pronto para

produção (Wells, 1999).

Cunningham (2005) define uma unidade de software como sendo a menor parte possível de

ser testada isoladamente em um sistema, onde tal isolamento significa que dada unidade possa

ser testada separada do resto da aplicação e de todas as demais unidades.

Conforme Wells (1999), ao criar os testes antes do código, espera-se que o desenvolvedor

encontre mais facilidade ao criar o código. Wells (1999) afirma que o tempo necessário para

criar um teste unitário e logo em seguida criar uma implementação para que o teste passe, é

praticamente o mesmo quando iniciando-se pela implementação.

Os testes unitários ajudam o desenvolvedor a pensar no que de fato deve ser feito, ou seja, os

requisitos do software são satisfeitos sob uma base forte de testes que garantem o

comportamento requerido (Wells, 1999).

Outro benefício dos testes unitários é o feedback imediato enquanto se desenvolve. Em

muitos casos, não fica claro quando o desenvolvedor terminou de fato toda a funcionalidade

necessária, principalmente quando dada funcionalidade passa por constantes mudanças de

escopo. Ao se criar testes unitários antes da implementação, fica claro ao desenvolvedor

quando a funcionalidade está pronta: quando todos os testes passarem (Wells, 1999).

21

Enquanto que em metodologias tradicionais de desenvolvimento de software, primeiro

constrói-se o software e depois testa-se o que foi construído, no XP, por utilizar-se o test first

(criação dos testes antes mesmo da implementação), todo o design do software é influenciado

pelo desejo de se testar tudo o que de fato gera valor para o cliente, deixando todo o sistema

mais facilmente testável do ponto de vista de negócios (Wells, 1999).

A maior resistência em se dedicar tanto tempo aos testes unitários são os curtos prazos para o

desenvolvimento do software. No entanto, o custo da criação antecipada de testes

automatizados não se compara ao custo de se criar testes após a descoberta de bugs (Wells,

1999). Wells (1999) afirma que o retorno que os testes automatizados proporcionam é muito

maior que o custo de criá-los. Além disso, os testes automatizados reduzem as chances de um

novo bug ser introduzido durante eventuais refatorações, pois a cada mudança nas unidades

do sistema é possível ter-se feedback rápido se essa mudança afetou alguma funcionalidade

existente do sistema (Wells, 1999), entretanto, a adição de novas funcionalidades ao sistema

geralmente exige que os testes unitários sejam adaptados para refletir a nova funcionalidade.

22

CAPÍTULO 3. FRAMEWORKS DE TESTES PARA JAVASCRIPT

Código JavaScript escrito para aplicações web tende a ter muitas dependências, pois

JavaScript puro geralmente só é útil quando combinado com HTML e CSS e por meio do uso

do DOM e do BOM (ZAKAS, 2009).

Por este motivo, além de o desenvolvedor ter de se atentar às diferenças entre engines

JavaScript, é necessário também se atentar às diferenças na forma em que uma página é

renderizada e como os elementos DOM podem ser manipulados entre diferentes ambientes

(ZAKAS, 2009). Isso por isso só já torna a tarefa de testar código JavaScript bastante onerosa.

Testes unitários, por definição, devem ser implementados com base no menor elemento

testável da funcionalidade do software a ser testada, e implica em testar a estrutura interna

(fluxo lógico e de dados) sem nenhuma dependência externa (ZAKAS, 2009). Nesse contexto,

os frameworks de testes auxiliam o desenvolvedor na preparação do ambiente propício aos

testes, com o devido isolamento da unidades a serem testadas, a um baixo custo de

manutenção.

3.1. xUNIT TEST FRAMEWORKS

Um tipo de framework bastante popular entre os desenvolvedores é o xUnit. O termo xUnit

refere-se a frameworks baseados nos conceitos do JUnit, o mais popular framework de testes

para Java e o SUnit, o framework de testes do Smalltalk (ZAKAS, 2009). Kent Beck, o pai do

Extreme Programming, é também um dos responsáveis pela criação de ambos frameworks.

Atualmente, existem diversas variações de xUnit para outras linguagens, como por exemplo o

nUnit, para C# e o JSUnit, para JavaScript. Embora o xUnit possa ser considerado o tipo mais

popular de framework de testes, os frameworks de BDD (do inglês, Behaviour-Driven

Development, em português, Desenvolvimento Guiado por Comportamento) teve um forte

crescimento em termos de popularidade nos últimos anos (ZAKAS, 2009).

23

3.1.1. BDD

Zakas (2009) afirma que a prática do TDD não está só relacionada ao uso de testes

automatizados, mas muito além disso, está relacionada ao design do software. Entretanto,

Zakas (2009) também afirma que devido a terminologia utilizada para descrever este

processo, muitos desenvolvedores acabam por se limitar a utilizar testes unitários para

verificar o funcionamento do sistema, e por esse motivo, nunca experimentaram de fato o

benefício de se usar os testes automatizados como ferramenta de design.

É nesse contexto que surge o BDD, que introduz um vocabulário mais rico e focado nos

aspectos comportamentais do sistema. Este vocabulário pode facilmente ser compartilhado

entre desenvolvedores, analistas de testes e de negócio, e clientes. Este vocabulário permite

que os aspectos importantes do sistema sejam transmitidos de forma satisfatória entre os

envolvidos no desenvolvimento de uma funcionalidade, permitindo que requisitos e

problemas sejam discutidos mais facilmente.

O processo de desenvolvimento com BDD se baseia em "estórias", as quais descrevem uma

funcionalidade que agregue valor ao cliente (Cohn, 2004), e que utilizam um vocabulário

familiar a todos os envolvidos no projeto (ZAKAS, 2009). O Cucumber, um dos frameworks

BDD mais populares, permite ao desenvolvedor utilizar estórias como testes executáveis, o

que permite que os testes aceitação sejam construídos junto ao cliente, aumentando as chances

de que as expectativas do cliente sejam completamente alcançadas com a entrega do software

(ZAKAS, 2009).

3.2. ESTRUTURA BÁSICA DE UM FRAMEWORK DE TESTES

Frameworks de testes tem uma estrutura bastante similar, independente do tipo de framework.

A estrutura básica de um framework de testes é composta pelos elementos a seguir.

3.2.1. Caso de teste, ou cenário de teste

Cada teste unitário é um caso de teste focado em um aspecto específico do comportamento

esperado de uma classe. Estes testes são na verdade métodos que irão exercitar o código

sendo testado, garantindo que determinada entrada de dados produzirá sempre a mesma saída

24

esperada. Por exemplo, o método de teste chamado testSomar() seria o teste unitário para o

método somar() da classe Calculadora.

Para garantir que o código testado produzirá sempre a mesma saída, dado o mesmo contexto,

são utilizadas fixtures para se criar o contexto do teste, e asserções para avaliar o resultado

obtido.

3.2.1.1. Asserções

Os frameworks xUnit permitem ao desenvolvedor criar um conjunto de métodos de teste

que podem ser executados e irão gerar um relatório da execução, exibindo as falhas nos

testes quando houver. O coração dos métodos de teste dos xUnit são as asserções, que

são chamadas de método que servem para garantir que o resultado da execução de

determinada parte do código produziu a saída esperada.

Um método de asserção bastante comum nos frameworks xUnit é o “assert equals“, que

possui dois parâmetros obrigatórios; o primeiro é o valor esperado, o segundo é o valor

atual, que geralmente é o retorno de uma a expressão. Caso o valor dos dois parâmetros

sejam diferentes entre si, uma exceção é lançada, quebrando o teste em questão (Zakas,

2009).

Figura 3 - Exemplo de um “assert equals” no framework JUnit, para Java

A sintaxe deste método possui inúmeras variações de acordo com a linguagem e o

framework utilizados. Asserções serão abordadas mais afundo no Error! Reference source Error! Reference source Error! Reference source Error! Reference source

not found.not found.not found.not found.

3.2.2. Fixtures

As fixtures separam o contexto de um teste. É uma convenção que os frameworks

disponibilizem métodos opcionais para que o desenvolvedor preencha as pré-condições de um

25

teste. Esses métodos são conhecidos como setUp() e tearDown(), onde o primeiro é usado

para criar o contexto do teste e o segundo é usado para retornar o ambiente ao seu estado

original. Ambos os métodos são independentes, ou seja, o desenvolvedor pode escolher

utilizar apenas o método setUp() ou vice-versa.

Embora a nomenclatura destes métodos varie de acordo com o framework, e em alguns casos

existam mais métodos além destes dois, o conceito de setUp e tearDown permanece o mesmo.

Em testes unitários de JavaScript, é comum que uma fixture seja uma extração de código

HTML com o qual o código JavaScript irá interagir.

3.2.3. Suíte de testes

Uma suíte de testes geralmente engloba os cenários de teste que utilizam a mesma fixture, no

entanto, em alguns frameworks é possível se utilizar em uma mesma suíte, vários conjuntos

de cenários, cada conjunto com suas fixtures.

Na prática, uma suíte de testes é um arquivo com a mesma extensão de uma classe da

linguagem sendo utilizada (.java para Java, .js para JavaScript, e etc), e que possui vários

métodos de teste (casos de teste). Por exemplo, o arquivo TestCalculadora.java seria uma suíte

de testes unitários para a classe Calculadora, e dentro desse arquivo, estariam os métodos

testSomar(), testDividir(), e etc, e opcionalmente os métodos setUp() e tearDown().

3.3. TEST RUNNERS

Zakas (2009) afirma que a parte mais importante em uma ferramenta de testes é o test runner,

pois é ele que determina o workflow quando se está desenvolvendo guiado por testes. O papel

do test runner é basicamente executar todo o código de teste, criado utilizando-se um

framework de testes unitários, e após a execução, apresentar um relatório da mesma.

Feedback rápido e claro sobre o resultados dos testes é uma exigência quando se está

trabalhando com testes unitários. O desenvolvedor precisa ter feedback rápido, pois o

processo de execução dos testes é algo que acontece inúmeras vezes durante o workflow, e

quando os testes falham, é necessário que o relatório de erros apresente o nível de detalhes

necessário para que o desenvolvedor identifique de forma rápida qual o motivo da falha.

26

Geralmente os frameworks da família xUnit possuem uma barra de progresso que mantem-se

verde enquanto os testes sendo executados estão passando, e a partir da primeira falha, ou

erro, a cor muda para vermelho, indicando que há um problema. A partir daí, o desenvolvedor

pode visualizar mais detalhes sobre a mesma, geralmente contendo a saída esperada e a saída

atual, ou uma stacktrace, em caso de exceções não tratadas (o caminho de uma exceção não

tratada através das camadas da aplicação).

No caso dos testes em JavaScript, existem dois tipos de test runners, os runners “in-

browser”, que são aqueles executados dentro de uma instância do navegador, e os runners

headless, que são aqueles que executam os testes em modo linha de comando, sem abrir uma

instância real de um navegador.

3.3.1. Runners “In-Browser”

Muitos frameworks possuem um runner in-browser nativo. Basicamente, o test runner é

invocado através de uma página HTML que carrega o framework e os testes unitários a serem

executados. Assim, o desenvolvedor pode acessar essa página no navegador desejado e

verificar se seu código JavaScript funciona naquele navegador.

A grande vantagem desses test runners é o fato de os testes rodarem em uma instância real do

navegador e a facilidade com que o desenvolvedor pode verificar o funcionamento do código

testado em diferentes navegadores e versões. Em contrapartida, abrir uma instância do

navegador e carregar manualmente a página do runner nos navegadores desejados é uma

tarefa onerosa e não atende a necessidade de feedback rápido no workflow com TDD.

Entretanto, é possível automatizar este processo.

Alguns exemplos frameworks de testes que possuem test runner in-browser nativo são o YUI

Test, mantido pelo time de desenvolvimento do Yahoo! (YAHOO, 2012) e o QUnit, mantido

pelo time de desenvolvimento do jQuery (JQUERY, 2012).

3.3.2. Runners Headless

Test runners headless por sua vez, não instanciam nenhum navegador, ao invés disso,

disponibilizam um utilitário de linha de comando que simula o comportamento de um

27

navegador real. O funcionamento deste tipo de test runner é muito semelhante aos dos

frameworks de testes das linguagens server-side.

As principais vantagens deste tipo de runner são (i) a velocidade com que os testes são

executados e (ii) a facilidade com que a execução dos testes pode ser automatizada e integrada

a um ambiente de Integração Contínua.

Um exemplo de runner headless é o env.js (ENVJS, 2012), desenvolvido originalmente pelo

criador do framework jQuery. Este runner possui sua própria implementação de navegador

(APIs DOM e BOM, etc) baseada no Rhino (MOZILLA, 2012b), uma implementação do

JavaScript em linguagem Java (ZAKAS, 2009). A combinação env.js + Rhino permite que os

testes de JavaScript sejam executados via linha de comando sem a utilização de um navegador

real.

Um problema apresentado por Zakas (2009) é que utilizando-se essas implementações, os

testes serão executados em um ambiente muito diferente de um ambiente de produção (os

navegadores dos usuários finais, por exemplo). Não somente o DOM é emulado, mas também

o JavaScript utilizado é uma implementação diferente. Sendo assim, essa combinação por si

só não é suficiente para garantir que o código alvo de testes irá funcionar em qualquer

navegador real.

28

CAPÍTULO 4. BUSTER.JS

Testes in-browser são difíceis de integrar a um workflow de desenvolvimento guiado por

testes, e testes headless por sua vez são mais fáceis de se trabalhar, neste contexto. No

entanto, testes headless geralmente não dão muita segurança quanto ao funcionamento em um

ambiente real (ZAKAS, 2009).

O Buster.js (BUSTERJS, 2012) é um framework de testes e test runner que se encontra em

fase beta, mas que já se mostra eficiente em abordar ambos os modos de execução, em um

ambiente de fácil configuração e poucas dependências externas.

O Buster.js é um pacote para o Node.js (NODEJS, 2012), um ambiente JavaScript para

servidores, portanto não é necessário baixar nenhum arquivo JavaScript, nem referenciar

nenhum arquivo JavaScript do Buster em páginas HTML. A única dependência deste

framework, é o Node.js instalado. Entretanto, é também possível utilizar o Buster.js como um

arquivo JavaScript simples, sem a necessidade de instalação do Node.js, como apresentado na

seção 4.2.

A sintaxe do Buster.js é muito similar à do RSpec, o popular framework BDD para Ruby. Sua

configuração é simples e sua sintaxe não requer que o desenvolvedor constantemente

referencie módulos nativos da linguagem, e isso deixa o código do teste mais limpo.

Figura 4 - Exemplo de caso de teste em Buster.js

A Figura 4 é um exemplo de um caso de teste simples. Em Buster.js, casos de teste são

chamadas ao método testCase do objeto buster, que por sua vez é uma instância do

framework de testes.

Como mostrado na Figura 4, o método testCase recebe como primeiro parâmetro o nome uma

String, que geralmente contém apenas o nome da classe alvo do teste, a partir daí, o

29

desenvolvedor adiciona os métodos de teste e os métodos setUp e tearDown, todos separados

por vírgula.

A Figura 5 e a Figure 6 mostram um caso de teste para uma classe Calculadora, escrita em

JavaScript. Por motivos didáticos, a classe Calculadora possui apenas o método soma, e

recebe apenas dois parâmetros, que são os números que devem ser somados.

Figura 5 – Calculadora.js: Classe Calculadora, em JavaScript

Figure 6 – Calculadora-test.js: Teste da class Calculadora, em JavaScript

Na Figure 6, a primeira linha contém uma variável declarada no escopo global do arquivo

Calculadora-test.js, essa variável calc é initializada no método setUp com uma instância da

classe Calculadora. Por ter sido declarada em um escopo global, essa variável estará acessível

a todos os métodos de testes que forem criados nesta classe de teste.

O arquivo Calculadora-test.js contém um único teste, que testa o funcionamento do método

soma da Calculadora apresentada na Figura 5.

4.1. INSTALAÇÃO E CONFIGURAÇÃO

Por ser um pacote para Node.js, sua instalação é bastante simples e possível por meio do

utilitário de linha de comando npm, que é um gerenciador de pacotes para Node.js. Sendo

assim, para instalar o Buster.js, utiliza-se o comando npm install -g buster em um prompt de

linha de comando. A instalação do Node.js não será abordada neste trabalho.

30

O Buster.js requer apenas um arquivo de configuração, onde são apontados os diretórios que

contém as classes de teste, as classes testadas, e quaisquer outras bibliotecas que sejam

dependências para os testes.

Figure 7 - Exemplo de estrutura de um projeto

A Figure 7 mostra a estrutura de um projeto de exemplo. A pasta src contém as classes de

negócio, e a pasta test contém as classes de teste. Segundo a convenção do Buster.js, para

cada classe X.js, haverá uma classe teste X-test.js

Para que o Buster.js consiga carregar as classes de suas pastas corretas, é necessária a

presença do arquivo arquivo de configuração test/buster.js. O conteúdo deste arquivo é

mostrado na Figura 8.

Figura 8 – Arquivo de configuração do Buster.js

No arquivo de configuração apresentado na Figura 8, rootPath, na linha 4, diz respeito ao

diretório base, relativo ao arquivo atual (test/buster.js), de onde os demais arquivos a serem

referenciados serão carregados. Caso omitido, é necessário adicionar o caminho relativo em

cada entrada de diretório subsequente (../lib, ../src, etc).

Na linha 5, environment diz respeito ao ambiente no qual os testes serão executados. Este

ambiente pode ser browser ou node. A diferença será explorada nos items a seguir.

31

Na linha 6, libs indica de onde o Buster.js irá carregar quaisquer bibliotecas que são

dependência para os testes, por exemplo, o jQuery.

Nas linhas 7 e 8, sources e tests indicam o local onde se encontram as classes de negócio e as

classes de testes respectivamente.

Após configurado, para executar os testes, basta executar o comando buster test a partir da

raíz do projeto, que no exemplo da Figure 7 seria a pasta meu_projeto_js.

4.2. MODO IN-BROWSER

O Buster.js oferece três diferentes formas de se executar testes in-browser, descritas a seguir.

4.2.1. Modo HTML

A forma mais simples de se executar testes no Buster.js é criando-se um documento HTML,

onde dentro de tags script, o desenvolvedor referencia a biblioteca do Buster.js e todos os

arquivos de teste, não sendo necessária a criação de um arquivo de configuração do Buster,

como mostrado na Figura 10. O exemplo desta seção utiliza as classes Calculadora.js e

Calculadora-test.js das figuras Figura 5 e Figure 6.

Figura 9 - Organização das classes e testes

32

Figura 10 - Arquivo buster.html

Feito isso, basta abrir o arquivo buster.html no navegador desejado para visualizar o relatório

dos testes, onde pode-se ver a quantidade de testes executados, a quantidade de sucessos,

asserções, falhas, erros, timeouts e o tempo total de execução, como mostrado na Figura 11.

Figura 11 - Relatório de execução dos testes

A Figura 12 apresenta um exemplo de falha durante a execução dos testes. É possível além da

contagem de sucessos e falhas, uma stack trace da falha, para auxiliar o desenvolvedor na

identificação o problema.

33

Figura 12 - Resultado de execução com falha

Embora este modo seja o modo mais simples e rápido de se obter feedback sobre os testes, ele

é demasiado manual para um workflow com TDD. Além disso, caso o desenvolvedor quisesse

verificar o funcionamento em outro navegador, seria necessário abrir o arquivo buster.html

manualmente no navegador desejado.

É importante ressaltar que o exemplo da calculadora não faz uso do DOM, então não haveria

muito ganho em se executar estes testes em diferentes navegador. Mais adiante será

apresentado um exemplo mais completo, em que o feedback em diferentes navegadores se

torna mais importante.

4.2.2. Modo estático

O modo estático é bastante parecido com o modo anterior, a diferença é que no modo estático

uma página HTML parecida com a da Figura 11 é remotamente servida pelo endereço

http://<url do servidor>:8282.

34

Para utilizar este modo, é necessário criar um arquivo de configuração como o da Figura 8 e

em um terminal de linha de comando, executar o comando buster static, como mostra a

Figure 13.

Figure 13 - Comando buster static

Acessando a url http://localhost:8282, tem-se a mesma página HTML da Figura 11. Neste

modo, não é necessário criar manualmente o arquivo buster.html mostrado na Figura 10, pois

a página com os resultados é gerada automaticamente pelo Buster.js.

Entretanto, este modo ainda não é o ideal em um workflow com TDD, pois de forma

semelhante ao modo HTML, no modo estático o desenvolvedor precisa abrir manualmente a

url com o relatório ons navegadores desejados e recarregar a página a cada alteração no

código.

4.2.3. Modo servidor

O modo server permite a automatização dos testes em diferentes navegadores. Este modo se

baseia no conceito master x slave, onde diferentes navegadores, em diferentes versões e

sistemas operacionais podem ser utilizados como slaves, onde os testes serão executados cada

vez que o desenvolvedor executar o comando buster test.

Nesta seção, será utilizado como exemplo uma classe JavaScript para validação de

formulários. Foi criado um formulário de login, com os campos usuário e senha, em que

apenas o campo usuário é obrigatório. A Figura 14 apresenta a estrutura do projeto utilizado

no exemplo.

35

Figura 14 - Estrutura do exemplo validação de formulário

Na Figura 14 pode-se observar o diretório test/fixtures. Este diretório contém os arquivos

HTML que serão utilizados nos testes. Estes arquivos de fixtures contém apenas uma extração

do HTML da página real, o suficiente para que o JavaScript sob teste possa ser exercitado por

completo. A Figura 15 exibe o conteúdo da fixture formulario.html.

Figura 15 - Conteúdo do arquivo test/fixtures/formulario.html

Para configurar o Buster.js de forma que as fixtures sejam carregadas corretamente, é

necessário adicionar a entrada resources ao arquivo buster.js, como mostra a Figura 16 na

linha 9.

36

Figura 16 - Configuração do Buster.js para uso de fixtures

Para que a classe de validação da Figura 15 funcione, é necessário que o formulário a ser

validado possua o atributo validate=”true”, e os campos obrigatórios precisam ter a classe

required como mostrado na linhas 1 e 3 da Figura 15, respectivamente.

Outro ponto a se observar na Figura 14 é a presença do arquivo jquery.min.js no diretório lib.

Este arquivo é necessário para que seja possível utilizar os helpers do jQuery para

manipulação do DOM. A Figura 17 apresenta a classe de validação criada para o exemplo em

questão.

37

Figura 17 - Conteúdo da classe ValidaForm.js

As figuras a seguir ilustram o efeito da classe de validação da Figura 17. Na Figura 18 é

apresentado o formulário em seu estado original, e na Figura 19, o mesmo formulário após

detectado erro na validação.

Figura 18 - Formulário de login do arquivo test/fixtures/formulario.html

38

Figura 19 - Formulário de login apresentando erro

O intuito da classe de validação é fazer com que os formulários que possuem o atributo

validate=”true” (Figura 18, linha 16) sejam validados ao clicar no botão submit, e fazer com

que quaisquer campos que possuam a classe required sejam validados ao deixar o campo

(evento blur, Figura 18, linha 23). O teste para a classe ValidaForm.js é apresentado a seguir,

na Figura 20.

Figura 20 - Clase de teste ValidaForm-test.js

39

Para automatizar a execução do teste da Figura 20, é necessário iniciar o serviço do Buster.js

e capturar os navegadores onde se deseja que os testes sejam executados. A inicialização o

serviço do Buster se dá através do comando buster server em um terminal de linha de

comando, como mostra a Figura 21.

Figura 21 - Comando buster server

A partir daí, para que o navegador desejado seja capturado. É necessário acessar a url exibida

na Figura 21 e clicar no botão de captura, como mostra a Figura 22.

Figura 22 - Página de captura de slaves

O inconveniente deste modo, é a necessidade de acessar a página de captura em todos os

navegadores desejados e capturá-los um a um. Porém, após feito isso, basta executar o

comando buster test a partir do diretório raiz do projeto e ver o resultado no terminal de linha

de comando, como mostra a Figura 23.

40

Figura 23 - Relatório da execução dos testes

É possível se observar no relatório de execução, em quais navegadores os testes foram

executados e em quais sistemas operacionais. No caso da Figura 23, os testes rodaram nas

versão 19 do navegador Firefox, na versão 25 do navegador Chrome, e na versão 6 do

navegador Safari. Caso não hajam slaves capturados, uma mensagem informativa é exibida,

como mostra a Figura 24.

Figura 24 - Tentativa de execução dos testes sem slaves capturados

A Figura 25 mostra um exemplo de execução com falhas.

Figura 25 - Relatório de execução com falhas

Uma grande vantagem deste método é a fácil integração no workflow com TDD, onde a cada

alteração de código, e a cada vez que o desenvolvedor desejar ter feedback sobre o

41

funcionamento do mesmo em diferentes navegadores, basta invocar o comando buster test e

aguardar a execução terminar.

4.3. MODO HEADLESS

Em um modo headless, os testes são executados sem que seja necessária nenhuma

instância real de um navegador, como descrito no item 3.3.2. No entanto, como citado no

início deste capítulo, o Buster.js encontra-se em versão beta, e na versão atual, está

funcionalidade ainda não está disponível, embora a própria documentação oficial apresenta

métodos alternativos.

4.4. MODO NODE

Além do modo headless, o modo Node permite que os testes sejam executados sem a presença

de navegadores, no entanto, neste modo o cenário de teste de fato não requer a presença de

um navegador, como no exemplo do teste da calculadora do item 4.2.1.

Para se executar os testes neste modo, algumas alterações são necessárias ao arquivo de

configuração do Buster.js. A primeira delas é alterar o valor da entrada environment, de

browser para node, como mostra a Figura 26, na linha 6.

Figura 26 - Configuração do Buster.js no modo node

Neste modo é necessário apenas indicar o diretório onde estão os arquivos de teste, não sendo

necessário utilizar as entradas lib e src (conforme visto na Figura 8), pois as classes que

forem dependência para os testes serão carregadas diretamente nos testes, utilizando o método

require, do Node.js. Até mesmo a biblioteca do Buster precisa ser carregada através do

método require, como pode ser visto na linha 2 da Figura 26.

42

São necessárias também mudanças tanto nas classes de negócio quanto nas classes de teste,

como mostram as figuras Figura 27 e Figura 28. Nas classes de negócio, é necessário utilizar

o método module.exports, para que a classe fique disponível fora do contexto do arquivo em

que a classe foi definida (Figura 27).

Figura 27 - Classe Calculadora.js alterada para o modo node

Nas classes de teste é necessário utilizar o método require para se carregar a classe de negócio

sendo testada (Figura 28).

Figura 28 Classe Calculadora-test.js alterada para o modo node

4.5. ASSERÇÕES

Os exemplos apresentados até aqui contém apenas o método de asserção assert.equals. No

entanto, o Buster.js conta com uma variada gama de métodos de asserção, que visam dar mais

valor semântico aos testes. Os principais métodos de asserção do Buster.js são brevemente

apresentados a seguir.

43

4.5.1. assert.same()

O método assert.same(objeto atual, objeto esperado) falha caso os dois objetos sendo testados não sejam referências à mesma instância.

4.5.2. assert.equals()

O método assert.equals(objeto atual, objeto esperado) falha caso os dois objetos sendo

testados não tenham o mesmo valor (em caso de valores primitivos), ou seus atributos

apresentem alguma diferença.

4.5.3. assert.greater()

O método assert.greater(valor atual, valor esperado) espera que o primeiro parâmetro seja

um valor maior que o segundo parâmetro.

4.5.4. assert.less()

Ao contrário do item anterior, o método assert.less(valor atual, valor esperado) espera que

o primeiro parâmetro seja menor que o segundo parâmetro.

4.5.5. assert.defined()

O método assert.defined(objeto) falha caso o objeto sendo testado seja undefined.

4.5.6. assert.isNull()

O método assert.isNull(objeto) falha caso o objeto sendo testado não seja null (nulo).

4.5.7. assert.isObject()

O método assert.isObject(objeto) espera que o parâmetro passado seja um objeto não nulo.

44

4.5.8. assert.isFunction()

O método assert.isFunction(função) espera que o parâmetro passado seja uma função.

4.5.9. assert.isTrue()

O método assert.isTrue(valor ou expressão) espera que o parâmetro passado resulte em um

valor true.

4.5.10. assert.isFalse()

Ao contrário do método anterior, este método espera que o parâmetro passado resulte em

um valor false.

4.5.11. assert.isString()

O método assert.isString(parâmetro) falha caso o parâmetro passado não seja do tipo

String.

4.5.12. assert.isBoolean()

O método assert.isBoolean(parâmetro) falha caso o parâmetro passado não seja um valor

booleano.

4.5.13. assert.isNumber()

O método assert.isNumber(parâmetro) falha caso o parâmetro passado não seja um valor

numérico.

45

4.5.14. assert.isNaN()

O método assert.isNaN(parâmetro) falha caso o parâmetro passado não seja um NaN.

4.5.15. assert.isArray()

O método assert.isArray(parâmetro) falha caso o parâmetro passado não seja um array.

4.5.16. assert.exception()

O método assert.exception(expressão) falha caso o parâmetro passado não gere uma

exceção.

4.5.17. assert.contains()

O método assert.contains(array, elemento) espera que o primeiro parâmetro contenha o

segundo parâmetro.

4.5.18. assert.tagName()

O método assert.tagName(elemento, nome da tag) falha caso o elemento passado no

primeiro parâmetro não possua a tag especificada no segundo parâmetro.

4.5.19. assert.className()

O método assert.className(elemento, nome classe) falha caso o elemento passado no

primeiro parâmetro não possua a classe especificada no segundo parâmetro.

Além dos métodos de asserção nativos do Buster.js, é possível criar novos métodos de

asserção e até mesmo sobrescrever os métodos existentes.

46

Este capítulo apresentou o framework Buster.js, primeiramente abordando a instalação e

configuração do mesmo e uma estrutura básica para seu funcionamento. A partir daí foram

abordados os diferentes modos de execução in-browser e uma alternativa ao modo headless,

utilizando-se Node.js, para os casos em que não é necessária a presença de um navegador.

47

CAPÍTULO 5. CONCLUSÃO

Este trabalho se propôs a investigar os fatores que tornam os testes automatizados em

JavaScript uma tarefa onerosa e propícia a erros. Para alcançar o objetivo, foi necessário

conhecer o estado atual dos frameworks de testes automatizados para JavaScript e verificar a

existência de uma ferramenta que se sobressaia às dificuldades encontradas e que seja útil no

fluxo de desenvolvimento com TDD.

Embora esteja em fase beta, o Buster.js se mostrou uma ferramenta bastante completa, por

cobrir diversos aspectos da execução de testes em JavaScript. O Buster.js possui test runners

nativos que permitem que os testes sejam rodados tanto em navegadores reais quanto sem a

presença de um navegador, quanto o código sendo testado não interage diretamente com o

DOM ou com o BOM.

No entanto, o Buster.js ainda não possui uma implementação que permite que testes que

interajam com o navegador, sejam executados em modo headless.

Um dos fatores que contribuem para que o Buster.js seja um framework tão completo, mesmo

estando em fase beta, é o fato de suas funcionalidades serem baseadas no funcionamento de

outros frameworks, como por exemplo o JsTestDriver, mas visando maior simplicidade e

flexibilidade.

Além de possuir uma sintaxe simples e bastante semelhante à de outros frameworks populares

de outras linguagens, o Buster.js, possui suporte ao estilo BDD de escrita de testes, além do

estilo TDD tradicional. Entretanto, apenas o estilo TDD foi apresentado neste trabalho.

Por fim, em relação a trabalhos futuros, considerando que a medida que o o número de testes

automatizados aumenta, maior o tempo de execução dos mesmos, um possível

direcionamento seria a busca por meios de execução dos testes em modo headless, por meio

da integração do Buster.js com outros test runners ou implementações headless do WebKit,

como o PhantomJS.

48

REFERÊNCIAS

BECK, K. et al. Manifesto for Agile Software Development. 2001. Disponível em: <http://www.agilemanifesto.org/>. Acesso em: 13 jan. 2012. BECK, K.; ANDRES C. Extreme Programming Explained, Embrace Change. 2. ed. Addison-Wesley Professional, 2004. 224.

BUSTERJS. Buster.js. 2012. Disponível em: <http://docs.busterjs.org/en/latest/overview>. Acesso em: 1 set. 2012. CIFANI, D. S. Introdução a testes automatizados em Ruby on Rails. 2011. Trabalho de Conclusão de Curso (Bacharel em Sistemas de Informação). Universidade Veiga de Almeida. 2011. COHN, M. User Stories Applied: For Agile Software Development. 2. ed. Addison-Wesley Professional, 2004. 304. CUNNINGHAM, W. Standard Definition Of Unit Test. 2005. Disponível em: <http://c2.com/cgi/wiki?StandardDefinitionOfUnitTest>. Acesso em: 29 ago. 2012.

ENVJS. Env.js. 2012. Disponível em: <http://www.envjs.com>. Acesso em: 1 set. 2012. FEITOSA, D. S. Um estudo sobre o impacto do uso de desenvolvimento orientado por testes na melhoria da qualidade de software. 2007. Trabalho de Conclusão de Curso (Bacharel em Ciências da Computação) Universidade Federal da Bahia. 2007. MOZILLA. JavaScript technologies overview. 2012. Disponível em: <https://developer.mozilla.org/en-US/docs/JavaScript_technologies_overview>. Acesso em: 29 ago. 2012. MOZILLA. Rhino. 2012. Disponível em: <https://developer.mozilla.org/en-US/docs/Rhino>. Acesso em: 1 set. 2012. NIST. The Economic Impacts of Inadequate Infrastructure for Software Testing. Disponível em: <http://www.nist.gov/director/planning/upload/report02-3.pdf>. Acesso em: 10 jan. 2012. NODEJS. Node.js. 2012. Disponível em: <http://nodejs.org>. Acesso em: 2 nov. 2012. PRESSMAN, R. S. Engenharia de Software. Makron Books, 2005.

JQUERY. QUnit: A JavaScript Unit Testing framework. 2012. Disponível em: <http://qunitjs.com>. Acesso em: 1 set. 2012.

49

SANTOS, R. L. Emprego de Test Driven Development no desenvolvimento de aplicacões. 2010. Trabalho de Conclusão de Curso (Bacharel em Ciencia da Computação). Universidade de Brasília. 2010. SOMMERVILLE, I. Software Engineering. Addison-Wesley Professional, 2003. TELES, V. M. Extreme Programming. 2008. Disponível em: <http://improveit.com.br/xp>. Acesso em: 13 jan. 2012. TELES, V. M. Desenvolvimento orientado a testes. 2008. Disponível em: <http://improveit.com.br/xp/praticas/tdd>. Acesso em: 13 jan. 2012. TELES, V. M. Um estudo de caso da adoção das práticas e valores do Extreme Programming. 2005. Dissertação (Mestrado em Informática) - Universidade Federal do Rio de Janeiro. 2005.

WELLS, D. Unit Tests. 1999. Disponível em: <http://www.extremeprogramming.org/rules/unittests.html>. Acesso em: 27 ago. 2012. ZAKAS, N. C. The curious case of JavaScript unit testing. 2009. Disponível em: < http://www.nczonline.net/blog/2009/11/17/the-curious-case-of-javascript-unit-testing>. Acesso em: 13 jan. 2012.

YAHOO. YUI Test. 2012. Disponível em: <http://yuilibrary.com/yui/docs/test/>. Acesso em: 1 set. 2012.

ZAKAS, N. C. Professional JavaScript for Web Developers. 2. ed. Wrox, 2009. 840.