DM JoseSampaio 2019 MEI
DM JoseSampaio 2019 MEI
DM JoseSampaio 2019 MEI
José Sampaio
Dedicatória
Resumo
Abstract
Enterprises need to look for and adopt techniques and approaches to the software deve-
lopment process in order to improve software quality metrics, reduce default rates, increase
team productivity and therefore produce quality software.
Currently, in the company where this case study will take place, there is a traditional approach
to the software development process, namely Test-Last Development. Therefore, there is
a need to explore and apply practices that improve the entire development flow, and the
relationship between software quality and team productivity impacts the business. With the
company’s exponential growth in recent years and the need to develop new functionality
over existing software, many of the solutions are moving toward legacy, becoming difficult
to maintain and scale. The scarcity of tools or processes that continually aid in the design
of solutions makes the whole process vulnerable to less correct decisions, which will have a
consequent impact on software quality and team productivity.
What this case study proposes to accomplish is the alteration of the current software de-
velopment process, with the introduction of Test-First methodologies, namely Test-Driven
Development, in collaboration with Behavior-Driven Development, which may contribute to
the resolution of problems highlighted.
The new development process is expected to contribute to continued software design, redu-
ced errors, increased software quality and, consequently, increased confidence in the software
being produced.
ix
Agradecimentos
Um forte agradecimento a todas as pessoas que contribuíam, desde o início, para a análise,
desenho e concretização deste estudo de caso, especialmente à equipa que abraçou este
desafio, criando todas as condições para que este projeto fosse levado a bom porto, um
profundo obrigado.
Muito obrigado ao orientador Professor Doutor Alexandre Bragança, pois sempre de uma
forma muito prestável e construtiva, ajudou continuamente para a realização desta disser-
tação.
Ao Instituto Superior de Engenharia do Porto e a todos os seus docentes que, durante a
Licenciatura em Engenharia Informática e Mestrado em Engenharia de Software, contribuí-
ram para o meu conhecimento e formação. Esta dissertação é o reflexo da aprendizagem
académica e profissional.
Por último, agradeço a mim mesmo pela persistência e convicção. Foi uma aprendizagem
fantástica durante este longo percurso.
Sem mais, um forte obrigado a todos.
xi
Conteúdo
Lista de Figuras xv
1 Introdução 1
1.1 Contexto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.2 Enquadramento . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.3 Test-Driven Development . . . . . . . . . . . . . . . . . . . . . . 2
1.1.4 Behavior-Driven Development . . . . . . . . . . . . . . . . . . . . 2
1.2 Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Análise de valor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5 Abordagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.6 Estrutura do documento . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 Estado da Arte 7
2.1 Testes de software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.1 Testes de unidade . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.1.2 Testes de integração . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.1.3 Testes de sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.1.4 Testes de aceitação . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2 Técnicas e ferramentas de teste . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.1 xUnit e NUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.2 Mock Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.3 Live Unit Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2.4 Selenium . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.2.5 Cucumber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.2.6 SpecFlow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.7 MockServer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.8 WireMock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2.9 Mongo2Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.2.10 AspNetCore Mvc Testing . . . . . . . . . . . . . . . . . . . . . . . 18
2.3 Indicadores e métricas de qualidade de testes . . . . . . . . . . . . . . . . 18
2.3.1 Code Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Tipos de Code Coverage . . . . . . . . . . . . . . . . . . . . . . . 19
xii
4 Análise e Design 47
4.1 Análise do estado atual . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.1.1 Processo de desenvolvimento . . . . . . . . . . . . . . . . . . . . . 47
4.1.2 Relação entre entidades no processo de desenvolvimento . . . . . . 49
4.1.3 Ferramentas e sistemas . . . . . . . . . . . . . . . . . . . . . . . . 50
4.1.4 Análise do problema . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.2 Estudo de soluções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.2.1 Introdução do TDD . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.2.2 Introdução dos testes de integração . . . . . . . . . . . . . . . . . 53
4.2.3 Introdução do BDD . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Cucumber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
SpecFlow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Selenium . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.2.4 Introdução aos Mutation Tests . . . . . . . . . . . . . . . . . . . . 55
4.2.5 Ferramentas de simulação aplicacional . . . . . . . . . . . . . . . . 56
AspNetCore Mvc Testing . . . . . . . . . . . . . . . . . . . . . . . 56
4.2.6 Ferramentas de simulação de APIs . . . . . . . . . . . . . . . . . . 56
MockServer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
WireMock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.2.7 Ferramentas de simulação de base de dados . . . . . . . . . . . . . 57
Mongo2Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
xiii
Docker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.3 Escolha da solução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
5 Implementação 65
5.1 Enquadramento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
5.2 Contexto do projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
5.3 Instanciação do processo . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.4 Treino sobre a equipa de desenvolvimento . . . . . . . . . . . . . . . . . . 67
5.5 Test-Driven Development . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.6 Behavior-Driven Development . . . . . . . . . . . . . . . . . . . . . . . . 72
5.7 Solução de testes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.7.1 Componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.7.2 Testes de unidade . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.7.3 Testes de integração . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.7.4 Testes de aceitação . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.8 Integração com CI/CD Pipeline . . . . . . . . . . . . . . . . . . . . . . . 82
6 Avaliação 85
6.1 Hipótese . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
6.2 Definição das grandezas . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
6.3 Definição das hipóteses . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6.4 Metodologias de avaliação . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6.4.1 Inquéritos de satisfação . . . . . . . . . . . . . . . . . . . . . . . . 86
6.4.2 Testes estatísticos . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.5 Atribuição dos métodos de avaliação . . . . . . . . . . . . . . . . . . . . . 88
6.6 Avaliação de resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
6.6.1 Satisfação da equipa . . . . . . . . . . . . . . . . . . . . . . . . . 89
6.6.2 Adoção da nova solução . . . . . . . . . . . . . . . . . . . . . . . 91
6.6.3 Produtividade da equipa . . . . . . . . . . . . . . . . . . . . . . . 93
6.6.4 Defeitos no código . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.6.5 Cobertura do código . . . . . . . . . . . . . . . . . . . . . . . . . 94
6.6.6 Tempo de execução da bateria de testes . . . . . . . . . . . . . . 95
7 Conclusão 99
7.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
7.2 Resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
7.3 Trabalho futuro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
7.4 Feedback adicional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Bibliografia 103
Lista de Figuras
Lista de Tabelas
Lista de Símbolos
1
Capítulo 1
Introdução
1.1 Contexto
1.1.1 Empresa
A empresa onde este estudo de caso se irá realizar é uma empresa de desenvolvimento de
software. O seu negócio opera sobre uma plataforma online, que procura estar sempre dis-
ponível, cem por cento operacional. Atualmente, a plataforma opera com inúmeros clientes,
destacando-se como o meio de interacção principal, tendo assim um enorme impacto no
negócio e por consequência no bom funcionamento da plataforma.
1.1.2 Enquadramento
Como uma das ambições da empresa é a construção de soluções robustas e escaláveis, existe
a necessidade de adotar e procurar novas metodologias para optimizar e facilitar o processo
de desenvolvimento de software, e como consequência, aumentar a qualidade das soluções
e produtos.
A área de negócio da empresa depende do software que é produzido, e tal como todas as
empresas que dispõem os seus serviços online, falhas no software, indisponibilidade, erros ou
incumprimento de prazos para entrega de iniciativas podem dar origem a perdas significativas,
o que afecta diretamente a área financeira. Assim sendo, o software produzido tem de ser
fiável, seguro e escalável.
2 Capítulo 1. Introdução
Após uma análise conjunta com a empresa sobre o estado atual do processo de desenvol-
vimento, concluiu-se que o resultado pode ser mais satisfatório com a introdução de novas
metodologias de trabalho, inferindo-se que existe a necessidade de melhorar o processo de
desenvolvimento de software que existe atualmente.
Este estudo de caso enquadra-se nesta necessidade e tem como objetivo dar resposta com
base nas boas práticas de Engenharia.
1.2 Problema
1.3 Objetivos
Este estudo de caso tem como principal objetivo responder à hipótese central desta disserta-
ção, Será que a aplicação do desenvolvimento orientado aos testes, em contexto empresarial,
permitirá melhorar a qualidade dos produtos e do processo de desenvolvimento de software?,
que surge após serem encontradas deficiências no processo de desenvolvimento atual.
O caminho delineado para ser possível avaliar esta hipótese passa pela introdução de um
novo processo de desenvolvimento de software, com base no TDD em colaboração com o
BDD, numa equipa de trabalho que não experimentou esta abordagem, com o propósito de
analisar o seu impacto na qualidade do software, redução de erros e na produtividade da
equipa durante o fluxo de desenvolvimento, de forma a responder aos problemas descritos
na secção 1.2.
Este estudo de caso pretende analisar o progresso da aplicação da nova metodologia adap-
tada à realidade da organização, e com base no resultado final, o objetivo será adotar o
novo processo de desenvolvimento, caso se revele uma mais valia para a equipa e para a
organização.
Assim sendo, os principais sub-objetivos serão:
• Desenhar um novo processo de desenvolvimento, introduzindo o TDD e BDD, no
contexto da organização.
• Implementar o processo TDD e BDD desenhado, através da sua aplicação por uma
equipa seleccionada, em caso de estudo.
4 Capítulo 1. Introdução
Uma nova abordagem para o processo de desenvolvimento com base no TDD e BDD prevê
uma melhor compreensão dos requisitos, um software mais fiável, com um bom desenho
e com menos falhas. O desenvolvimento será mais fluido, facilitando o trabalho de todos
os intervenientes que compõe o processo de desenvolvimento de software, como Develo-
pers, Team-lead, Manager, Product Owner e Quality Assurance. O aumento da confiança
e da efetividade do software será um consequente muito importante também a ter em con-
sideração. Parente este cenário, valor será criado para as partes envolvidas no processo,
nomeadamente a empresa e a equipa de desenvolvimento, e como consequente, clientes
finais que irão usufruir dos produtos com mais qualidade.
A análise de valor do estudo em questão será detalhada na secção 3.2.
1.5 Abordagem
Capítulo 2
Estado da Arte
Neste capítulo serão abordados os conceitos teóricos mais relevantes, apresentando métodos
e abordagens conhecidas que irão servir de base na resolução do problema em questão.
O estado da arte está dividido em secções, sendo elas testes de software, onde irão ser in-
troduzidos os principais tipos de teste, técnicas e ferramentas de teste, contextualizando as
ferramentas existentes para o apoio aos testes de software, integração contínua, ferramen-
tas utilizadas para automatizar o processo de entrega de software, indicadores e métricas de
qualidade de testes, metodologias de desenvolvimento de software, processos de desenvol-
vimento de software e por fim, revisão das técnicas TDD e BDD a nível industrial.
2018). Este é um forte indicador de que o código deverá ser redesenhado. Os testes de
unidade relevam bem mais vantagens do que apenas simples validações de comportamento.
Na realidade, quando a escrita do teste de unidade envolve muitas instruções e é difícil
de realizar, poderemos estar presentes a um caso de necessidade de desacopolamento e
refactor.
Segundo (Reese, A. D. George e Wenzel 2018), as características de um bom teste de
unidade são:
• Rapidez, um teste de unidade deve demorar na ordem dos milissegundos.
• Isolado e independente, sem necessitar de nenhuma dependência ou fator externo.
• Repetível, resultando sempre no mesmo resultado, independentemente do número de
vezes que executa.
• Auto-verificável e oportuno, o teste deve ser capaz de detetar se passou ou falhou, de
forma automática.
O teste de unidade, tal como a escrita de qualquer código deve seguir normas para que possa
ser também um código organizado. "Organizar, Agir e Assertar é um padrão comum quando
se testa determinada unidade. Como o nome indica, consiste em três ações principais:
Organizar os objetos de forma a criar e configurar conforme necessário para o caso de teste,
atuar sobre o objeto e confirmar que o resultado é o esperado"(Reese, A. D. George e
Wenzel 2018).
"Arrange, Act, Assert", este padrão permite que diferentes momentos sejam separados,
ficando bem definido quais as etapas que o teste contém e a responsabilidade de cada uma.
Na figura 2.1 está presente um exemplo que segundo (Reese, A. D. George e Wenzel 2018)
segue o padrão dos três A’s.
1 [ Fact ]
2 p u b l i c v o i d Add_TwoNumbers_ReturnsSumOfNumbers ( )
3 {
4 // A r r a n g e
5 var s t r i n g C a l c u l a t o r = CreateDefaultStringCalculator () ;
6
7 // Act
8 v a r a c t u a l = s t r i n g C a l c u l a t o r . Add ( " 0 , 1 " ) ;
9
10 // A s s e r t
11 Assert . Equal (1 , a c t u a l ) ;
12 }
"A legibilidade é um dos aspetos mais importantes ao escrever um teste. Separar cada uma
dessas ações dentro do teste, destaca claramente as dependências necessárias para chamar
o código, como o código está a ser chamado e o que se está a tentar afirmar, [...] o objetivo
principal é tornar o teste o mais legível possível"(Reese, A. D. George e Wenzel 2018).
Estes autores defendem também a utilização de uma normalização para a nomenclatura dos
testes de unidade, que passa por agregar o nome com o nome do método que está a ser
testado, o cenário a ser testado e por fim, o comportamento esperado do teste.
10 Capítulo 2. Estado da Arte
O objetivo prático de cada fase de teste é detetar erros que provavelmente não serão de-
tetados pelas fases de testes anteriores. Neste caso, o teste de integração deve ter como
objetivo detetar erros que podem não ser encontrados durante o teste de unidade, ou seja,
o teste é aplicado quando dois ou mais módulos individuais são combinados para formar um
programa ou um componente de software. O teste é feito ao nível do módulo, ou invés
do nível de instrução como nos testes de unidade (Leung e White 1990). Assim sendo,
o objetivo é validar a integração de módulos, a comunicação entre interfaces e avaliar o
resultado final do processamento entre os módulos (Luo 2001)
Segundo (Leung e White 1990), após a conclusão do teste de unidade, os módulos devem ser
integrados. Durante os testes de integração, os três principais pontos a validar são: missing
function, extra function e interface errors. Estes erros acontecem quando o software foi
mal desenhado ou implementado. Um exemplo dado por (Leung e White 1990) na ajuda à
deteção destes problemas passa por integrar o módulo A com o módulo B:
1. Garantir que A não utilizará funcionalidades que não possam ser fornecidas por B
missing function.
2. Assegurar que A não utilizará outras funcionalidades de B se B fornecer mais funções
do que aquelas requeridas por A extra function.
3. Garantir que os padrões de interface entre A e B sejam preservados interface errors.
(Leung e White 1990) defende ainda a integração de apenas dois módulos. Ao integrar mais
de dois módulos, podemos integrá-los de forma incremental, adicionando um novo módulo
ao conjunto de módulos integrados, de forma sequencial.
O teste de sistema (System test ou End-to-End) tem como objetivo afirmar a qualidade do
sistema de "ponta a ponta". É focado na funcionalidade do sistema integrado do ponto de
vista do utilizador final. Esta metodologia pressupõe que tanto os testes de unidade como
os teste de integração sejam executados e aprovados (Tsai et al. 2001).
O teste do sistema é geralmente baseado na especificação funcional dos requisitos do sis-
tema. Requisitos não funcionais como confiança, segurança e facilidade de manutenção,
também são verificados (Luo 2001). O principal objetivo é verificar se o software inte-
grado, incluindo subsistemas internos e sistemas de suporte externos, apoia coletivamente
as principais funções de negócio da organização (Tsai et al. 2001).
Esta secção irá apresentar as técnicas e ferramentas de teste, utilizadas ao longo deste
estudo de caso, que servem de auxilio ao processo de escrita, manutenção e compreensão
dos testes.
NUnit é uma ferramenta para realizar testes de unidade (Stolberg 2009) para tecnologias
Microsoft .Net. Esta é uma ferramenta open-source e tem o mesmo propósito que o JUnit
para a linguagem Java. A ferramenta NUnit é baseada na família de arquiteturas xUnit,
especializada em fornecer uma base para testes de unidade ou testes de API (Application
Program Interface), sendo o NUnit também uma ferramenta de teste API (Butt, Bhatti e
Sarwar 2017). "xUnit é uma ferramenta de teste open-source, focada na comunidade para
.NET Framework. Escrito pelo autor original do NUnit, xUnit é a última tecnologia para
testes de unidade C#, F#, VB.NET e outras linguagens"(Foundation n.d.).
O teste de unidade é escrito na ferramenta de teste NUnit sobre um projeto de teste que
faz referência à Dynamic Linked Library (DLL) em que uma API é baseada. Além disso, a
estrutura da ferramenta NUnit deve ser configurada no projeto de teste. Esta ferramenta
permite anotações sobre os teste, decorando-os de acordo com os atributos disponíveis,
facilitando a leitura e compreensão do teste (Butt, Bhatti e Sarwar 2017). No excerto 2.2,
podemos observar um exemplo de um teste à classe PrimeServiceIsPrimeShould usando o
NUnit.
1 u s i n g N U n i t . Framework ;
2 u s i n g Prime . S e r v i c e s ;
3
4 namespace Prime . UnitTests . Services
5 {
6 [ TestFixture ]
7 public class PrimeServiceIsPrimeShould
8 {
9 private readonly PrimeService primeService ;
10
11 public PrimeServiceIsPrimeShould ()
12 {
13 p r i m e S e r v i c e = new P r i m e S e r v i c e ( ) ;
14 }
15
16 [ Test ]
17 public void ReturnFalseGivenValueOf1 ()
12 Capítulo 2. Estado da Arte
18 {
19 var r e s u l t = primeService . IsPrime (1) ;
20
21 A s s e r t . I s F a l s e ( r e s u l t , " 1 s h o u l d n o t be p r i m e " ) ;
22 }
23 }
24 }
Segundo (Butt, Bhatti e Sarwar 2017), a execução do conjunto de testes no NUnit compila
resultados que podem ser exportados para fins de personalização, por exemplo, o relatório
XML. O resultado dos testes aparecem a verde em caso de sucesso, a falha é destacada a
vermelho e os testes ignorados são sinalizados a amarelo.
4
5 r e s . setContentType ( " t e x t / html " ) ;
6 P r i n t W r i t e r out = r e s . g e t W r i t e r () ;
7
8 try{
9 i n t tempf = I n t e g e r . p a r s e I n t ( s t r f ) ;
10 d o u b l e tempc = ( t e m p f − 3 2 ) ∗ 5 . 0 / 9 . 0 ;
11 o u t . p r i n t l n ( " F a h r e n h e i t : " + t e m p f + " , C e l s i u s : " + tempc ) ;
12 }
13 catch ( NumberFormatException e ) {
14 out . p r i n t l n ( " I n v a l i d temperature : " + s t r f ) ;
15 }
16 }
Listing 2.3: Conversor de Fahrenheit para Celsius (D. Thomas e Hunt 2002)
No excerto 2.4, podemos observar o teste de unidade usando Mock Objects. Com a utiliza-
ção desta prática, é possível simular um determinado comportamento sobre as dependências.
Neste caso em concreto, o objetivo seria simular uma falha na dependência e validar o retorno
da função doGet.
1 p u b l i c void testBadParameter () throws Exception {
2 T e m p e r a t u r e S e r v l e t s = new T e m p e r a t u r e S e r v l e t ( ) ;
3 M o c k H t t p S e r v l e t R e q u e s t r e q u e s t = new M o c k H t t p S e r v l e t R e q u e s t ( ) ;
4 M o c k H t t p S e r v l e t R e s p o n s e r e s p o n s e = new M o c k H t t p S e r v l e t R e s p o n s e ( ) ;
5
6 r e q u e s t . s e t u p A d d P a r a m e t e r ( " F a h r e n h e i t " , " boo ! " ) ;
7 r e s p o n s e . setExpectedContentType ( " t e x t / html " ) ;
8 s . doGet ( r e q u e s t , r e s p o n s e ) ;
9 response . v e r i f y () ;
10 a s s e r t E q u a l s ( " I n v a l i d t e m p e r a t u r e : boo ! \ r \ n " , r e s p o n s e .
getOutputStreamContents () ) ;
11 }
Listing 2.4: Teste de unidade com Mock Objects (D. Thomas e Hunt 2002)
(Mackinnon, Freeman e Craig 2000) refere ainda um conjunto de padrões para a utilização
desta prática, como criação de instâncias de objetos Mock, definição do estado nos objetos
Mock, definição das expectativas nos objetos Mock, invocação do código de domínio com
objetos Mock como parâmetro, e por fim, verificação da consistência dos objetos Mock.
Segundo (Marri et al. 2009) e (Mackinnon, Freeman e Craig 2000), esta prática incentiva a
testes mais estruturados e reduz o custo, proporcionando um formato comum nos testes de
unidade que é fácil de aprender e compreender, simplificando a depuração e fornecendo testes
que detetam o ponto exato da falha no momento em que um problema ocorre. Mock Objects
por vezes é a única forma de testar código que depende de um estado difícil ou impossível
de reproduzir, ou seja, testes com Mock Objects melhora o código do domínio, preservando
o encapsulamento, reduzindo as dependências globais e esclarecendo as interações entre as
classes.
Live Unit Testing é um Plug-in, desenvolvido para o IDE (Integrated Development Envi-
ronment) Visual Studio, que executa automaticamente todos os testes em segundo plano,
14 Capítulo 2. Estado da Arte
Exemplo de execução do Live Unit Testing no Visual Studio O Live Unit Testing ajuda
a ver rapidamente se o código que está a ser escrito está coberto e se os testes que o cobrem
estão a executar com sucesso. Os resultados dos testes e a visualização de cobertura são
visíveis em cada linha de código, representado por três estados diferentes (Blog 2017):
• Uma linha de código executável que é coberta por pelo menos um teste com falha é
decorada com um "x"a vermelho.
• Uma linha de código executável que é coberta apenas por testes e com aprovação é
decorada com um "v"a verde.
• Uma linha de código executável que não é coberta por nenhum teste é decorada com
um traço "-̈"a azul.
2.2. Técnicas e ferramentas de teste 15
2.2.4 Selenium
O Selenium é uma ferramenta de automação de testes de Web que usa scripts para exe-
cutar testes sobre um navegador web. Funciona à base de JavaScript e iframes para in-
corporar o mecanismo de automação de testes ao navegador, permitindo a utilização de
multi-plataformas (Gojare, Joshi e Gaigaware 2015). O Selenium fornece um conjunto de
instruções como: abrir um URL, clicar sobre um elemento ou inserir valores sobre uma caixa
de entrada de texto, tal como podemos observar no excerto 2.5. Também fornece um
conjunto de comandos que permitem a verificação a especificação de valores ou comporta-
mentos esperados (Holmes e Kellogg 2006).
1 @Test
2 public void testConfirmDismiss ()
3 {
4 // C l i c k i n g b u t t o n w i l l show a C o n f i r m a t i o n A l e r t w i t h OK and
5 // C a n c e l B u t t o n
6 WebElement b u t t o n = d r i v e r . f i n d E l e m e n t ( By . i d ( " c o n f i r m " ) ) ;
7 button . c l i c k () ;
8 try {
9 // Get t h e A l e r t
10 A l e r t a l e r t = d r i v e r . switchTo () . a l e r t () ;
11 // C l i c k C a n c e l b u t t o n , by c a l l i n g d i s m i s s ( ) method o f
12 // A l e r t C l a s s
13 a l e r t . dismiss () ;
14 // V e r i f y Page d i s p l a y s c o r r e c t m e s s a g e on D i s m i s s
15 WebElement m e s s a g e = d r i v e r . f i n d E l e m e n t ( By . i d ( "demo" ) ) ;
16 a s s e r t E q u a l s ( "You D i s m i s s e d A l e r t ! " , m e s s a g e . g e t T e x t ( ) ) ;
17 } catch ( NoAlertPresentException e ) {
18 e . printStackTrace () ;
19 }
20 }
Segundo (Umesh e Saraswat 2015), testes de Selenium são fáceis de escrever, permitindo
a identificação de elementos usando os objetos DOM do navegador. Os testes devem ser
independentes e autónomos. A utilização de práticas como background, SetUp e TearDown
ajuda na redução da duplicação de código, colocando a aplicação no estado desejado para
testar um conjunto de funcionalidades.
No estudo realizado por (Umesh e Saraswat 2015), concluiu-se que o seguinte conjunto de
passos ajuda na fluidez dos testes de Selenium, facilitando a captação e compreensão dos
requisitos.
• Escreva os testes antes do desenvolvimento.
• Após o desenvolvimento, coloque-os a verde.
• Por fim Refactor, reduz a duplicação do código e aumentar a capacidade de manuten-
ção do conjunto de testes.
2.2.5 Cucumber
teste, enquanto a componente executável dos casos de teste é gravada em código Ruby.
Funciona com Java, Ruby, .NET, Flex ou aplicações Web (Wynne, Hellesoy e Tooke 2017).
1 F e a t u r e : Cash W i t h d r a w a l
2 S c e n a r i o : S u c c e s s f u l l w i t h d r a w a l f r o m an a c c o u n t i n credit
3 G i v e n : I h a v e d e p o s i t e d $180 i n my a c c o u n t
4 When I r e q u e s t $20
5 Then $20 s h o u l d be d i s p e n s e d
Cucumber com Selenium O Cucumber pode ser utilizado com Selenium, ou seja, o
Cucumber permite saber através de texto simples o que um teste deve verificar e o Selenium
faz chamadas diretas para o navegador web e executa as etapas (Yurtoğlu 2018). Com a
utilização destas duas ferramentas em conjunto, as instruções técnicas executadas pelo
Selenium ficam "escondidas", apenas ficando visíveis os passos escritos com Cucumber,
que irão ser mapeadas todas as etapas para uma função ou um método (Wynne, Hellesoy
e Tooke 2017) que o Selenium irá executar. (Yurtoğlu 2018) refere que a lacuna entre
a tecnologia e o negócio é preenchida, pois o Cucumber através a sua linguagem simples
permite que qualquer pessoa com um background não técnico compreenda os passos dos
testes.
2.2.6 SpecFlow
O SpecFlow é uma ferramenta que permite que equipas de desenvolvimento que traba-
lham sobre .NET definam, executem e mantenham testes de aceitação automatizados. O
SpecFlow é baseado no Gherkin e faz parte do sistema ecológico do Cucumber. Utiliza
uma linguagem que permite a que as entidades de negócio e as entidades mais técnicas se
compreendam entre si (Specflow 2019).
Os testes de aceitação no SpecFlow seguem o paradigma do BDD. Os testes de aceitação
podem então ser testados automaticamente. A sua especificação serve como documentação
viva do sistema (Specflow 2019), um aspeto importante que visa também colmatar a falta
de documentação existente de software.
O SpecFlow integra-se no Visual Studio, permitindo que o conceito BDD possa ser aplicado
dentro do ecossistema .NET.
2.2.7 MockServer
O MockServer é uma ferramenta que pode ser usada para simular qualquer servidor ou serviço
que use o protocolo HTTP/HTTPS (Bloom 2018), ou seja, o MockServer é considerado
um proxy que substitui o comportamento de um sistema real com base em configurações.
Segundo (Bloom 2018), quando o MockServer recebe um pedido, ele corresponde ao pedido
em relação às expectativas ativas que foram configuradas. Uma expectativa define a ação
2.2. Técnicas e ferramentas de teste 17
que é executada. Um exemplo pode ser, retornar uma determinada resposta quando um
determinado pedido é realizado.
(Bloom 2018) afirma que o MockServer suporta as seguintes ações:
• Retornar uma resposta "simulada"quando uma solicitação corresponde a uma expec-
tativa.
• Encaminhar uma solicitação quando a solicitação corresponder a uma expectativa.
• Executar um retorno de uma chamada quando uma solicitação corresponder a uma
expectativa, permitindo que a resposta seja criada dinamicamente
• Retornar uma resposta inválida ou fechar a conexão quando uma solicitação corres-
ponder a uma expectativa.
• Verificar se os pedidos foram efetivamente enviados.
Segundo (Bloom 2018), o MockServer é bastante útil em cenários onde é necessário recriar
todas as dependências externas para que os testes sejam independentes, fáceis e rápidos,
isolando a bateria de testes da aplicação alvo, permitindo a que quando os testes falhem,
se descubra uma falha interna do sistema e não uma falha ou erro de um sistema externo.
Com a facilidade de criação de expectativas e verificação de solicitações a determinados
recursos fazem desta ferramenta uma forma de desacoplar a aplicação a ser testada das suas
dependências, permitindo validar e similar todas as operações reais efetuadas pela aplicação
a ser testada.
2.2.8 WireMock
O WireMock é uma ferramenta que tem como propósito simular APIs baseadas no protocolo
HTTP/HTTPS para que no contexto de testes, estes sejam rápidos, robustos e abrangentes.
WireMock pode ser considerado como uma ferramenta de virtualização de serviços, ou seja,
permite que independentemente das APIs externas estarem a funcionar ou não, continuar o
trabalho sem bloqueios (Minimal e Akehurst 2019).
Segundo (Minimal e Akehurst 2019), esta ferramenta representa um servidor que pode ser
configurado para responder a pedidos específicos e capturar pedidos, para que possam ser
verificados posteriormente. Pode ser usado como uma biblioteca por qualquer aplicação
JVM ou ser executado como um processo independente no mesmo host que o sistema em
teste ou num servidor remoto. Todos os recursos do WireMock são acessíveis através de
sua interface REST e pela sua API Java.
2.2.9 Mongo2Go
O Mongo2Go é uma abstração em torno dos binários mais recentes do MongoDB. Funciona
com Windows, Linux e macOS. Este pacote Nuget contém os executáveis do mongod,
mongoimport e mongoexport (Mongo2Go 2019a).
Este recurso permite que uma aplicação desenvolvida sobre a framework .NET e linguagem
C# possa instalar e usufruir de um simulador do MongoDB sem que seja necessária a
instalação ou virtualização do MongoDB.
Segundo (Mongo2Go 2019a), o Mongo2Go possui dois casos de uso:
• Fornecer várias bases de dados MongoDB temporárias e isolados para testes de unidade
ou de integração.
• Fornecendo uma base de dados MongoDB de configuração rápida para um ambiente
de desenvolvimento local.
Indicadores e métricas de qualidade podem ajudar a perceber quão fortes são os nossos
testes, qual o grau de segurança que temos sobre o código, entre outros. Segundo (Zhang
2012), o principal objetivo de construir testes de software é melhorar a qualidade do software.
A métrica ajuda a avaliar a qualidade dos testes do software, a fim de avaliar o quão bem o
teste foi feito. É importante medir as partes críticas que realmente são úteis, selecionando
medições específicas de acordo com as necessidades.
Code Coverage é um indicador que mede a cobertura dos testes sobre o código de produção,
apontando quais partes do código não estão a ser contempladas pelos testes. Como conse-
quente, expõe as funcionalidades implementadas pelo código que não estão a ser testadas
adequadamente ou na sua totalidade, sendo que não vão ser testadas na sua íntegra até que
o utilizador final as use. Por norma, um bug não pode ser encontrado se não for executado
e quanto mais cedo um bug for encontrado, mais rápido e mais simples pode ser corrigido
(Burr e Young 1998). Developers e testers usam o code coverage para garantir que todas
ou substancialmente todas as declarações de um programa tenham sido executadas pelo
menos uma vez durante o processo de teste tikir2002efficient.
Segundo (Marick et al. 1999), o code coverage é um indicador poderoso, exemplificando
com "se uma linha nunca foi executada, é uma aposta segura que não foi verificado se existe
2.3. Indicadores e métricas de qualidade de testes 19
um bug escondido". Marick alerta também para o facto de que o indicador é facilmente
contornável, caso sejam escritos testes fracos com asserts fracos para cobrir as instruções do
código, fazendo com que se atinja uma maior percentagem de cobertura. "Estar testado"não
é a mesma coisa que "exercitar todas as condições de cobertura", ou seja, é necessário
fazer com que todas as expressões lógicas avaliem tanto o verdadeiro como o falso. O code
coverage não diz quais os testes que são necessários escrever, apenas pode dizer qual o
código existente com cobertura.
O code coverage é portanto um indicador que nos mostra em percentagem as instruções
cobertas pelos testes sobre o total de instruções do código. Segundo (Marick et al. 1999),
projetar este indicador inicialmente para atingir 100% de cobertura não é uma boa prática,
pois será uma meta que levará, por norma, há criação de testes fracos para atingir a métrica
estabelecida ao invés de criar testes fortes que validem efetivamente todas as expressões
lógicas.
Contudo, o code coverage é um indicador muito importante para medir tanto a cobertura
dos testes sobre o código fonte como para alertar quais as instruções que não estão a ser
validadas pelos testes. Segundo (Marick et al. 1999), este indicador tem de ser bem utilizado
e interpretado, pois apenas serve para o propósito a que se compromete, não para expor
quais os testes que são necessários de escrever.
Segundo (Santelices et al. 2009), (Elbaum, Gable e Rothermel 2001) e (Fallah, Devadas e
Keutzer 2001) existem três grandes tipos de code coverage:
• Statement coverage (cobertura sobre instrução) mede as instruções que foram exe-
cutadas de forma isolada.
A seguinte função representa como é feito o calculo do statement coverage.
Number of executed statements
%Of statement cov er age = X 100
T otal number of statements
O conceito de mutation test foi introduzido na década de 1970, como uma técnica baseada
em mutações que mede a eficácia da deteção de falhas dos testes com base na introdução
de falhas de forma consciente, com o propósito de reforçar a qualidade dos testes que
cobrem as instruções do código fonte. Durante o processo de mutation test, as falhas são
introduzidas num programa através da criação de várias versões desse mesmo programa, cada
uma das quais contendo uma mutação. O termo mutação nasceu da prática de alteração do
programa original, sendo que as versões defeituosas do programa são mutantes do original, e
um mutante é dado como "morto"quando um caso de teste falha, satisfazendo a necessidade
20 Capítulo 2. Estado da Arte
de identificar um caso de teste útil (Offutt 1994). Um mutante "vivo", mostra um caso
em que o conjunto de testes falha potencialmente no exercício de detetar erros e, portanto,
precisa de ser melhorado de forma a conseguir capturar o mutante (DeMillo, Lipton e
Sayward 1978). Os mutation test dão uma visão sobre a qualidade dos testes, refletindo a
necessidade de melhorar o conjunto de testes ou, evidenciando a sua qualidade na cobertura
do código fonte.
Segundo (Offutt 1994), existem duas categorias após cada caso de teste ter sido executado
contra cada mutante vivo:
• O mutante é funcionalmente equivalente ao programa original. Um substantivo equi-
valente sempre produz a mesma saída que o programa original, portanto, nenhum caso
de teste pode matá-lo.
• O mutante é eliminável mas o conjunto de casos de teste é insuficiente para eliminá-lo.
Nesse caso, novos casos de teste precisam ser criados e o processo itera até que o
conjunto de testes seja forte o suficiente para satisfazer o testador.
A pontuação de mutação para um conjunto de dados de teste é a percentagem de mutantes
não equivalentes mortos por esses dados. Um conjunto de dados de teste de mutação é
adequado se a sua pontuação de mutação for de 100% (Offutt 1994).
Apesar de todas as vantagens dos mutation tests, segundo (Jia e Harman 2011), esta
técnica é considerada como uma técnica de teste dispendiosa em termos computacionais.
Na sua publicação, foi feita uma análise às técnicas de mutation tests mais relevantes e
mais utilizadas, sendo elas:
• Mutant Sampling é uma abordagem simples que escolhe aleatoriamente um pequeno
subconjunto de mutantes de todo o conjunto.
• Mutant Clustering é uma técnica com base na escolha de um subconjunto de mutantes,
ou seja, os mutantes são agrupados em clusters sendo que cada mutante do mesmo
cluster tem a garantia de ser morto por um conjunto de casos de teste semelhante.
Apenas um pequeno número de mutantes é selecionado de cada grupo para ser usado
nos testes. Os mutantes restantes são descartados.
• Selective Mutation tem por base a redução do número de operadores de mutação
aplicados, procurando encontrar um pequeno conjunto de operadores de mutação que
geram um subconjunto de todos os possíveis mutantes sem perda significativa da
eficácia do teste. Operadores de mutação geram diferentes números de mutantes e
alguns operadores de mutação geram muito mais mutantes do que outros, muitos dos
quais podem se tornar redundantes.
• Higher Order Mutation é a técnica de encontrar mutantes de ordem superior, raros,
mas valiosos, que denotam falhas subtis. No tradicional Teste de Mutação, os mutan-
tes podem ser classificados em mutantes de primeira ordem (FOMs) e mutantes de
ordem superior (HOMs). Os FOMs são criados aplicando um operador de mutação
apenas uma vez. HOMs são gerados pela aplicação de operadores de mutação mais
de uma vez.
Estas técnicas assentam sobre o conjunto de premissas "fazer menos", "fazer mais rápido"e
"fazer de forma inteligente", com o objetivo de reduzir o número de mutantes, identificar
os pontos críticos e criar mutantes mais robustos e com propósito lógico.
2.4. Integração contínua 21
2.4.2 Jenkins
"O Jenkins é um servidor de automação independente e open-source que pode ser usado
para automatizar todo o tipo de tarefas relacionadas com a criação, teste e distribuição ou
implementação de software. [...] é um produto altamente extensível, cuja funcionalidade
pode ser estendida através da instalação de plugins"(Jenkins 2018). Pode ser instalado
através do sistema nativo, Docker ou até mesmo executado de forma independente por
qualquer máquina com o Java Runtime Environment (JRE) instalado (Jenkins 2018).
O Jenkins é portanto uma ferramenta de integração contínua, pode executar vários scripts
como Ant e Maven e scripts de shell para ambientes Windows e Unix/Linux. A cada
compilação (builds), podem ser gerados relatórios de teste para que possíveis problemas
possam ser facilmente detetados e compreendidos. Os relatórios são gerados de acordo
com a estrutura de testes, que embora por omissão exista o JUnit, outras estruturas de
teste são suportadas através de plugins. As ferramentas da gestão de controlo de código
suportadas são: CVS, Subversion, Git, Mercurial, Perforce e Clearcase. Os builds podem
ser ativados por commits, sondagem ao repositório, manualmente, através de uma API, etc.
2.5. Métodos ágeis de desenvolvimento de software 23
Para que toda a orquestração do Jenkins seja feita, o Jenkins disponibiliza uma página Web
para que a manipulação e construção de componentes, feedback e configuração seja possível
de forma fácil (Hembrink e Stenberg 2013).
2.4.3 Docker
O Docker é uma tecnologia de virtualização de containers (C. Anderson 2015), aberta para
desenvolvimento, envio e execução de aplicações. O Docker permite separar as aplicações da
infraestrutura para que seja possível entregar software rapidamente, sendo possível reduzir
significativamente o atraso entre a gravação do código e sua execução em produção (Docker
overview 2019).
O Docker fornece a capacidade de empacotar e executar uma aplicação sobre um ambiente
isolado chamado de container (Docker overview 2019), sendo este caracterizado como
uma máquina virtual muito leve. O Docker permite que sejam criadas aplicações dentro
de containers de forma a que sejam partilhadas através do Docker Hub, repositório central
de containers. Desta forma, qualquer developer pode executar containers que reproduzem
ambientes de produção (C. Anderson 2015).
O isolamento e a segurança permitem a execução de muitos containers simultaneamente
sobre um determinado host. Os containers são leves porque não precisam da carga extra
de um hipervisor, mas são executados diretamente no kernel da máquina host (C. Anderson
2015).
Esta secção irá apresentar as metodologias ágeis de desenvolvimento de software que servirão
de auxilio para a compreensão deste estudo de caso, nomeadamente o Extreme Program-
ming, Scrum e Test-Last Development. O Extreme Programming apresenta-se como uma
das primeiras metodologias ágeis de desenvolvimento de software (Jeffries, A. Anderson e C.
Hendrickson 2001), e o Scrum como uma metodologia ágil de gestão de projetos, também
aplicada ao desenvolvimento de software.
Segundo (Beck e Fowler 2001), o Extreme programming (XP) consiste na integração cons-
tante e testes automatizados, com pequenos lançamentos frequentes que incorporam feed-
back contínuo do cliente final. O trabalho em equipa faz do XP uma metodologia excecio-
nalmente flexível e eficaz para o desenvolvimento de software. "Antes considerada radical,
o Extreme Programming (XP) está rapidamente a tornar-se reconhecida como uma aborda-
gem particularmente adequada para equipas pequenas que enfrentam requisitos vagos ou que
mudam rapidamente, ou seja, a maioria dos projetos no atual mundo de desenvolvimento
de software acelarado".
A filosofia chave do XP: O planeamento não é um evento único, mas um processo constante
de reavaliação e correção de curso ao longo do ciclo de vida do projeto (Beck e Fowler
2001). Dentro deste contexto de flexibilidade e mudanças rápidas, o planeamento é crítico.
24 Capítulo 2. Estado da Arte
Sem isso, os projetos de software podem desmoronar rapidamente. Escrito por autores
reconhecidos do XP Kent Beck e Martin Fowler, o XP apresenta métodos e abordagens
necessárias para planear e gerir um projeto.
As práticas de XP não incluem a preparação de extensos requisitos ou documentação do
projeto antes de iniciar o desenvolvimento. Consequentemente, o XP depende muito da
comunicação contínua entre as partes interessadas, dos constantes ciclos de feedback para
esclarecer e especificar a implementação dos requisitos e desta forma, responder às mudanças
de forma orgânica (Layman et al. 2006). O XP apresenta-se como uma metodologia ágil,
com o propósito de proporcionar flexibilidade às equipas de desenvolvimento de software
e a todos os outros intervenientes. Permite a mudança e adaptação dos requisitos sem
comprometer o custo que isso possa causar às partes interessadas.
2.5.2 Scrum
O SCRUM tem como principal objetivo gerir projetos que se encontram numa situação onde
é difícil planear com antecedência e os ciclos de feedback constituem o elemento central,
podendo os requisitos mudar mais regularmente (Schwaber e Beedle 2002). O SCRUM não
é apenas um método de desenvolvimento iterativo e incremental, é também um método de
desenvolvimento de software adaptativo, fornecendo técnicas de auto-organização, organi-
zação de processos, criação de conhecimento, etc (Beedle et al. 1999). Esta metodologia
apresenta-se assim como uma metodologia ágil, onde tem a capacidade de criar equipas
auto-suficientes que conseguem responder rapidamente à mudança. Segundo (Beedle et al.
1999), o SCRUM permite-nos construir software mais suave, pelo que não adianta tentar
escrever todos os requisitos antecipadamente, sendo que novos problemas surgem no sis-
tema em um novo contexto e como tal, precisamos de outra abordagem mais suave para
construir software.
Os SCRUM é caracterizado como sendo uma prática onde o software é desenvolvido por uma
equipa auto-organizada em sprints, podendo ter a duração de entre 2 semanas a 4 semanas,
sendo que cada sprint começa com o planeamento e termina com uma revisão (Schwaber e
Beedle 2002). O objetivo é entregar o máximo de software de qualidade possível dentro de
uma sprint (Beedle et al. 1999). As iniciativas que irão compor o sprint são provenientes do
backlog, componente que é responsável por conter as iniciativas que irão ser realizadas pela
equipa de desenvolvimento. Depois de reunir e redefinir prioridades as iniciativas restantes e
2.6. Processos de desenvolvimento de software 25
novas, um novo backlog é formado e um novo sprint é iniciado. A entidade responsável por
decidir quais as iniciativas a entrar no sprint é o Product Owner, sendo ele quem compõe
a próxima sprint, que irá ser executada pela equipa de desenvolvimento. O seu papel é
gerir e executar todas as iniciativas da sprint, e quando existe um bloqueio ou problema, o
scrum master é responsável por resolver os problemas que impedem a equipa de trabalhar
efetivamente, destacando-se também como o elemento desbloqueador (Schwaber e Beedle
2002).
A figura 2.2 apresenta a linguagem ou termos padrão que são utilizados no SCRUM, e a
relação entre eles. Segundo (Beedle et al. 1999) a "socialização do conhecimento"leva a
uma estrutura de equipa auto-organizada, onde o processo é inventado de maneira adaptável
no dia a dia. No final de cada sprint, há uma demonstração para mostrar ao cliente o valor
que foi concretizado e entregue (EngageCustomer), proporcionar à equipa de desenvolvi-
mento a sensação de realização (CompensateSuccess), integrar e testar uma parte razoável
do software que está a ser desenvolvido (EngageQA) e por último garantir progresso real
que consiste na redução do backlog (NamedStableBases).
Segundo (Beedle et al. 1999), o SCRUM é composto por reuniões que tem como propósito
gerir os vários momentos da equipa e da sprint, não existindo um processo pré-definido dentro
de um sprint, em vez disso, as reuniões conduzem a conclusão das atividades alocadas,
permitindo que a equipa de desenvolvimento "socialize o conhecimento dos membros da
equipa". O conjunto de reuniões é composto por uma reunião antes de se iniciar o sprint,
para que sejam discutidas as iniciativas que irão compor o próximo sprint. Durante a sprint,
os membros da equipa tem uma reunião diária, tipicamente de quinze minutos, onde debatem
e coordenam o trabalho, expondo quais os itens concluídos, os bloqueios que estão a impedir
a progressão e a gestão de atribuição de tarefas aos membros da equipa. Por fim, é realizada
uma última reunião para fazer a revisão da sprint.
Desenvolver e manter sistemas de software envolve uma vasta variedade de atividades rela-
cionadas entre si, e de modo a gerir todo o processo vários modelos foram desenvolvidos ao
longo dos anos. Estes incluem o modelo Waterfall, Desenvolvimento iterativo, Prototyping,
Modelo espiral e RAD. Após esta era, os modelos de processo ágeis como XP, Scrum e Kan-
ban começaram a ganhar a sua relevância, pelo facto de proporcionarem menos stress na
análise e no design, a implementação começa muito cedo no ciclo de vida do desenvolvimento
de software e permite que as equipas mudem rapidamente às mudanças. O desaparecimento
de modelos mais antigos são fruto das rápidas mudanças e adaptações ao ambiente de de-
senvolvimento, permitindo a evolução para modelos que melhoram a qualidade das equipas
de desenvolvimento de software (Kaur e Sengupta 2013).
As secções seguintes irão abordar processos de desenvolvimento de software enquadrados
em metodologias ágeis, como o Test-Driven Development, o Acceptance Test-Driven De-
velopment e por fim o Behavior-Driven Development.
O TDD apareceu inicialmente como parte da disciplina Extreme Programming 2.5.1, descrita
em Exetreme Programming Explained, publicado em 1999. Em 2002, Beck lançou Test-
Driven Development: By example e Dave Astels Test-Driven Development: A Practical
Guide, formando a base desta metodologia.
Como referido na secção 1.1.3, o TDD propõe a inversão do fluxo tradicional de software,
que é desenvolver primeiro e escrever os testes no final. Segundo (Beck 2003), o TDD
é uma forma de gerir o medo durante o processo de desenvolvimento de software. Uma
metáfora que representa aquilo a que o TDD se compromete a resolver: Ao invés de ser um
processo experimental, o objetivo é começar a aprender e desenvolver mais concretamente,
evitando a ocultação de problemas e bloqueios, ou seja, comunicar de forma mais clara e
aberta, indo ao encontro de feedback útil e concreto.
O TDD chamou a atenção de uma grande comunidade de developers de software que con-
sidera ser uma forma rápida e eficaz de produzir código fiável. o TDD incorpora elementos
de design e refactor, fazendo com que o processo de desenvolvimento de software flua. E
como outras artes, a única maneira de realmente entender o TDD é praticá-lo (Jeffries e
Melnik 2007). E para que possa ser praticado, o TDD precisa de ser entendido na sua
integra. Segundo (Beck 2003), o TDD é definido pelo seguinte conjuntos de premissas:
• Escrever sempre um teste automático que falhe antes de escrever qualquer código.
• Eliminar a duplicação.
• Desenhar soluções organicamente, com o código em execução fornecendo feedback
entre as decisões.
• O autor do código deve escrever os próprios testes.
• O ambiente de desenvolvimento deve dar uma resposta rápida a pequenas alterações.
• Os projetos devem consistir em muitos componentes altamente coesos e fracamente
acoplados, de forma a facilitar o teste.
2.6. Processos de desenvolvimento de software 27
Característica Definição
Fáceis de escrever Um teste fácil de escrever dá indícios de que o software está
simples e preciso. Se existir complexidade ao escrever o teste,
podemos estar presente a um cenário em que o código poderá
não estar correto.
Fáceis de ler Os testes serão mais valiosos se forem legíveis, dando uma se-
gunda perspectiva sobre qual o comportamento do código fonte.
Rápidos de executar Os testes devem dar uma rápida resposta às mudanças, e para
isso o tempo de resposta tem de ser curto.
Isolamento O sucesso ou o fracasso de um teste seria irrelevante para outros.
Determinísticos Dado um conjunto de entradas, o resultado deve ser sempre igual.
Parciais É esperado poder escrever os testes uns de cada vez.
Combináveis É expectável poder executar os testes com qualquer combinação.
Versionáveis A fonte dos testes deve funcionar corretamente com o resto da
fonte no sistema.
À priori Devemos ser capazes de escrever os testes antes de serem exe-
cutados.
Automáticos Os testes devem ser executados sem intervenção humana.
Úteis quando se Escrever os testes à priori deve ser uma experiência de apren-
pensa em design dizagem. (Jeffries e Melnik 2007) firmam que o TDD é uma
atividade de design e programação, não uma atividade de teste
em si.
Assim sendo, quem pratica o TDD tem acesso às iterações curtas e rápidas que esta meto-
dologia permite, observando as mudanças e o resultado que cada operação reflete, de forma
segura e eficaz (Janzen e Saiedian 2008).
Uma das partes mais importantes do TDD é a realização de testes. A abordagem do TDD
amplia a afirmação feita por Boris Beizer em 1983 (Binder 2000): “O ato de designar testes
é um dos mais eficazes detetores de bugs conhecidos”. São eles que permitem validar o
comportamento do software, dar feedback contínuo e contribuem para o nível de confiança
da aplicação. Segundo (Beck 2003), a tabela 2.1 apresenta as principais características que
os testes devem ter.
Red, Green, Refactor O processo de alto nível que constitui o TDD é designado como
"Red, Green, Refactor". Segundo (Beck 2003) este ciclo repetitivo é o a base do TDD,
permitindo que seja aplicado de forma correta. “À medida que seguimos esse ciclo simples, o
programa cresce e o design evolui com ele“ (Jeffries e Melnik 2007). No início de cada ciclo,
todos os testes tem de estar aprovados obrigatoriamente, com exceção do que está a ser
criado, o responsável pelo desenvolvimento deste novo ciclo. No final do ciclo, é expectável
que o developer execute todos os testes, garantindo que todos fiquem aprovados. Na figura
2.3 é apresentado este ciclo de forma ilustrativa.
Como podemos observar na figura 2.3, os três componentes podem ser sintetizados da
seguinte forma segundo (Beck 2003):
28 Capítulo 2. Estado da Arte
• Vermelho (Fail): envolve a escrita de um pequeno teste que não funciona, talvez nem
compile no início.
• Verde (Pass): envolve o teste funcionar rapidamente, implementando o código fonte.
• Refactor : envolve eliminar toda a duplicação criada apenas para fazer o teste funcionar
e alterações de código para melhorar o design, passo a passo.
Cada nova unidade de código requer uma repetição deste ciclo. O TDD é uma metodo-
logia de programação em que as atividades de codificação, teste e design são fortemente
entrelaçadas (Guidelines for Test-Driven Development 2006). Todos os testes criados são
executados de forma contínua para garantir que novas alterações não comprometem o com-
portamento já validado. Com isto, evita-se regressões manuais e trabalho humano.
O seguinte exemplo dado por (Beck 2003) reflete todos os passos necessários para seguir o
padrão "Red, Green, Refactor"representado na figura 2.3:
1. Escreva um novo caso de teste.
2. Execute todos os casos de teste e veja o novo falhar.
3. Escreva apenas código suficiente para fazer o teste passar.
4. Volte a executar os casos de teste e veja todos a passarem.
5. Refactor para remover a duplicação e melhorar o design.
Durante o ciclo de vida útil de um software, a tendência é cair para o estado de legacy, a
menos que o processo de refactor seja feito de forma contínua. Sem um conjunto de testes
de regressão completos, o refactor é praticamente impossível porque não será possível saber
quais partes do software são afetadas por uma alteração. O TDD inclui refactor contínuo,
de modo a que o desenho e o código seja sempre mantido (Kumar e Bansal 2013).
2.6. Processos de desenvolvimento de software 29
TDD na correção de bugs O ciclo de "Red, Green, Refactor"tanto pode ser aplicado ao
desenvolvimento de novas funcionalidades como na correção de problemas (Ficco, Pietran-
tuono e Russo 2011).
O seguinte processo é um exemplo de como pode um bug ser resolvido tendo em conta as
premissas do TDD.
1. Escrever um teste que falha devido ao bug.
2. Corrigir o bug no código.
3. Correr o teste e verificar que ficou aprovado.
4. Correr todos os testes e verificar a aprovação.
Considerado por muitos autores como (Jeffries e Melnik 2007), (Kumar e Bansal 2013),
(Ficco, Pietrantuono e Russo 2011) e (Janzen e Saiedian 2008), a correção e prevenção de
erros é uma das maiores valias do TDD.
A figura 2.4 representa o ciclo do ATDD. A equipa cria um ou mais testes de aceitação para
uma história ou recurso antes de dar início ao trabalho sobre ele, sendo que normalmente
esses testes são discutidos e capturados no momento em que a equipa está a trabalhar
com as parte interessadas, processo que serve para entender ou capturar um requisito que
será uma história no backlog. Os testes de aceitação tornam-se requisitos executáveis,
30 Capítulo 2. Estado da Arte
fornecendo feedback continuo, dando uma indicação clara do progresso que a equipa está
a alcançar. Um requisito é satisfeito se todos os testes ou critérios de aceitação associados
forem satisfeitos. No ATDD, os testes de aceitação podem ser automatizados.
No caso de estudo de (E. Hendrickson 2008), concluiu-se que as equipas que implementaram
o ATDD evidenciaram que o ato de definir testes de aceitação no momento de discussão
dos requisitos resulta em uma melhor compreensão. Os testes do ATDD obrigam a chegar
a um acordo concreto sobre o comportamento exato que o software deve ter. Equipas que
seguem todo o processo, automatizando os testes à medida que implementam o recurso,
geralmente descobrem que o software resultante torna-se fácil de testar e em geral, adicionar
novos testes é um processo trivial.
Apesar das vantagens que o ATDD pode proporcionar a quem o adota, segundo (Solis
e Wang 2011) muitos developers confundem-se ao usar TDD e ATDD em simultâneo,
sentindo que a metodologia se tornava confusa, pois não há um processo bem definido para
colocar em prática o ATDD. Alguns dos problemas do TDD e ATDD são que eles estão
focados em verificar o estado do sistema, ao invés de comportamento desejado do sistema.
O facto de a linguagem natural para descrever casos de teste não ser estruturada, faz
com que exista liberdade para que cada um "normalize"de acordo com a sua interpretação,
tornando os casos de teste difíceis de entender.
a sua entrega trará, facilitando a descrição dos requisitos, eliminando alguma redundância
ou incerteza.
Utilizando o exemplo dado por (Introducing BDD 2006), na tabela 2.2 podemos observar
um exemplo de um cenário de teste, e com base nesse exemplo, existem vários cenários a
considerar: a conta tem crédito suficiente, a conta não tem crédito, a conta contém crédito
mas a caixa multibanco não, etc. Para a definição destes cenários, o BDD defende também
uma nomenclatura específica, sendo que o comportamento de uma história será ditada pelo
seu critério de aceitação. O conjunto de pontos seguintes representa o modelo para capturar
os critérios de aceitação de uma história.
• Given [Q]
• When [W]
• Then [E]
Q representa o contexto inicial do cenário, o W é a fase em que todos os eventos ocorrem
e por fim, o E é a fase de validação dos resultados.
Usando o modelo Given-When-Then, os dois primeiros cenários podem ser representados
pela seguinte forma, segundo (Introducing BDD 2006):
1 Given the account i s i n c r e d i t
2 And t h e c a r d i s v a l i d
3 And t h e d i s p e n s e r c o n t a i n s c a s h
4 When t h e c u s t o m e r r e q u e s t s c a s h
5 Then e n s u r e t h e a c c o u n t i s d e b i t e d
6 And e n s u r e c a s h i s d i s p e n s e d
7 And e n s u r e t h e c a r d i s r e t u r n e d
Seguindo este contexto, o BDD revela capacidade de compreensão sobre todas as partes
interessadas, criando uma linguagem comum que facilita a compreensão e a comunicação.
Com a descrição dos cenários, a entrega de valor é claramente identificada. O processo
32 Capítulo 2. Estado da Arte
de desenvolvimento acaba por beneficiar deste modelo, pois permite um maior foco nos
requisitos e no cenário, evitando desperdício.
A seguinte revisão das técnicas TDD e BDD a nível industrial tem como objetivo apresentar,
sobre um determinado contexto, experiências, desafios e resultados de diferentes estudos de
caso realizados em empresas de desenvolvimento de software.
2.7.1 TDD
Esta secção irá apresentar o resultado de casos de estudo a nível industrial onde foi aplicado
o TDD e por fim um conjunto de fatores que limitam a sua adoção.
(Rendell 2008) apresenta um caso de estudo, realizado na T-Mobile, sobre um projeto de
longa duração incorporado numa equipa de trabalho que utilizava metodologias ágeis. A
iniciativa de aplicar TDD foi apresentada à equipa como opcional, não existindo a obrigato-
riedade de o aplicar. O projeto em questão tinha uma data bem definida para sua entrega
(Date-Driven). Como conclusões, (Rendell 2008) refere que o TDD "parece ser uma téc-
nica que muitos developers estão cientes, mas falta a experiência necessária que lhes permite
utilizá-lo de forma mais eficaz". Quando os developers obtêm a experiência necessária no
TDD e o aplicam no seu dia-a-dia, melhoram a estrutura do código. (Rendell 2008) refere
ainda que o TDD não foi responsável pelo sucesso do projeto, foi no entanto, uma das
muitas técnicas importantes aplicadas pela equipa que permitiram que a T-Mobile atingisse
um grau de adaptabilidade mais elevado do que a exibida por projetos semelhantes, sem
sacrificar seu compromisso e a sua qualidade. Como observações, (Rendell 2008) relata
que: a aplicação dogmática do TDD poderia levar ao aumento de custos sem o retorno
correspondente do investimento, para developers mais experientes, o uso do TDD ajuda a
tornar o código mais testável, e por fim, "um developer pode ser brilhante o suficiente para
produzir código de baixo defeito e bem estruturado sem codificar um teste primeiro. Isso
não ajuda os developers subsequentes, possivelmente menos capazes, a manter esse mesmo
código sem o suporte da alta cobertura de teste".
2.7. Revisão das técnicas TDD e BDD a nível industrial 33
O estudo de caso apresentado por (Nagappan et al. 2008) realizado sobre quatro equipas
de desenvolvimento de software (três estudos da Microsoft e um na IBM respetivamente)
que adotaram a prática TDD, relata que o TDD foi aplicado e adaptado aos processos
da empresa em questão e não sobre o contexto de XP. As equipas trabalhavam sobre a
metodologia Waterfall. Como resultado deste estudo de caso, o TDD mostrou-se aplicável
"em vários domínios e pode reduzir significativamente a densidade de defeitos do software
desenvolvido, sem redução significativa da produtividade da equipa de desenvolvimento.
Versões futuras dos produtos desenvolvidos sobre a prática TDD, à medida que continuam
com uso do TDD, também terão baixa densidade de defeitos devido ao uso ativo de testes
de unidade e integração que esta prática impõe às soluções de software"(Nagappan et al.
2008). Os resultados dos estudos indicam que a densidade de defeitos dos quatro produtos
diminuiu entre 40% e 90% em relação a projetos semelhantes que não utilizaram a prática
de TDD. Subjetivamente, as equipas evidenciaram um aumento de 15 a 35% no tempo de
desenvolvimento inicial após a adoção do TDD.
(Jeffries e Melnik 2007) apresenta uma revisão de estudos industriais e académicos sobre
equipas e projetos que adotaram o TDD, projetos estes legacy e não legacy sobre diferentes
linguagens de programação (Java, C++, C# ...). A nível industrial, a maioria dos estudos
de caso concluiu: aumento significativo da qualidade do software, diminuição da tolerância
a falhas e um aumento da produtividade. Apenas dois casos da amostra analisada deram
como inconclusivo o impacto do TDD na qualidade do software.
(B. George e Williams 2003) realizou um conjunto de experiências estruturadas com 24
developers profissionais. Um grupo desenvolveu o código usando TDD, enquanto o outro,
uma abordagem semelhante ao modelo Waterfall (Cascata). Como resultado, (B. George
e Williams 2003) refere que a abordagem TDD parece produzir código com qualidade acen-
tuada, os developers do grupo TDD gastaram mais tempo (16%) do que os developers do
grupo Waterfall nos seus desenvolvimentos. Em média, 80% dos developers afirmaram que
o TDD é uma abordagem eficaz e 78% acreditam que a abordagem melhora a sua produti-
vidade. "Qualitativamente, esta pesquisa também descobriu que a abordagem TDD facilita
o design mais simples e que a falta de design inicial não é um obstáculo. No entanto, para
alguns, a transição para a mentalidade do TDD é difícil"(B. George e Williams 2003).
(Janzen 2005) apresenta uma pesquisa sobre estudos empíricos de engenharia de software
aplicados em ambiente profissional para avaliar a influência do TDD na qualidade do software,
demonstrando que os programadores que usam o TDD produziram código que passou até
50% mais de testes externos do que o código produzido pelos grupos de que não usam o
TDD. Houve também uma diminuição significativa no grupo que utilizou TDD no tempo
gasto tem depuração (debug) em relação ao outro grupo. Por último, a complexidade
computacional é muito menor e o volume e a cobertura do teste são maiores no código
realizado em TDD.
(Causevic, Sundmark e Punnekkat 2011) aponta sete razões mais evidentes que limitam a
adoção do TDD em ambiente industrial, com base em cerca de 48 casos de estudo:
• Maior tempo de desenvolvimento: Tempo necessário para implementar um determi-
nado conjunto de requisitos. Este pode ser um fator crítico e decisivo quando as orga-
nizações ponderam a sua adoção. Segundo (Causevic, Sundmark e Punnekkat 2011),
34 Capítulo 2. Estado da Arte
dependendo da maturidade da organização, uma perda inicial (neste caso, maior tempo
de desenvolvimento) pode ofuscar um ganho a longo prazo (por exemplo, diminuição
do tempo total do projeto ou aumento da qualidade do produto - ambos relatados
em muitos estudos). Portanto, a pressão organizacional interna pode arriscar o uso
adequado de TDD.
• Pouca experiência/conhecimento do protocolo TDD: A falta de treino, formação e
prática fazem com que o pouco conhecimento deste protocolo se torne um obstáculo
a quem a quer adotar. (Kumar e Bansal 2013) refere ainda que "como o TDD é uma
nova abordagem, não temos muita experiência nesse domínio em comparação com a
abordagem tradicional".
• Design insuficiente: Este fator refere-se ao processo de desenhar o software. Com base
em (Causevic, Sundmark e Punnekkat 2011), alguns casos de estudo apontam para
problemas relacionados com a adoção do TDD, referindo que a qualidade do desenho
diminui, principalmente em projetos de maior escala e complexos. No entanto, não
há apoio empírico em massa que mostre que a falta de desenho deve ser considerado
como um fator limitante para a adoção industrial do TDD.
• Pouca experiência/conhecimento na realização de testes por parte dos developers:
Capacidade do developer produzir testes automáticos de forma eficiente e eficaz.
O TDD é uma técnica onde os testes guiam o desenvolvimento, e como tal, se o
conhecimento for escasso na realização de testes, pode comprometer a adoção e a
boa prática do TDD.
• Adesão insuficiente ao protocolo TDD: O TDD é um protocolo que é composto por
diretrizes bem definidas e exatas, como a escrita de um teste automatizado antes da
escrita do código, e este fator reflete a pouca adesão a estas diretrizes. Segundo
(Causevic, Sundmark e Punnekkat 2011), as razões para abandonar o protocolo TDD
incluem pressão de tempo, falta de disciplina, desvio das diretrizes do TDD e prazos
curtos de desenvolvimento.
• Limitações específicas do domínio e da ferramenta: problemas técnicos na implemen-
tação do TDD, ou seja, dificuldades na realização de testes automatizados, (em GUIs
por exemplo). O TDD requer suporte de ferramentas que proporcionem as condições
necessárias à realização e execução de testes automáticos, o suporte adequado de
ferramentas é vital para a adoção bem-sucedida do TDD.
• Código legacy : código existente que representa anos ou décadas de desenvolvimento
e investimento e serve como backbone para produtos existentes e futuros. Kent Beck
quando apresenta o TDD não refere como aplicá-lo em código legacy, transpondo a
impressão de que todo o código é construído do zero. Segundo (Causevic, Sundmark
e Punnekkat 2011), como isso raramente acontece nas empresas de desenvolvimento,
a adoção do TDD pode ser problemática. A falta de bateria de testes automatizados
dificulta a flexibilidade fornecida pelo feedback rápido que o TDD impõe.
2.7.2 BDD
Esta secção irá apresentar o resultado de casos de estudo a nível industrial onde foi aplicado
o BDD.
2.7. Revisão das técnicas TDD e BDD a nível industrial 35
(Pereira et al. 2018) apresenta um estudo empírico realizado com 24 profissionais com
experiência prática em BDD, de diferentes empresas e com diferentes níveis de especialização
em desenvolvimento ágil, de forma a identificar os benefícios e os desafios do BDD. Este
estudo recolheu dados sobre como o BDD era entendido, como o BDD era aplicado, quais
os seus desafios e quais os benefícios sobre diferentes projetos, concluindo que "o BDD
melhora a comunicação e a colaboração em todas as funções de desenvolvimento de software,
incluindo clientes", o BDD permite também que um projeto tenha living documentation
(documentação viva), documentação que está sempre atualizada, sendo isto possível porque
a abordagem do BDD depende do uso de cenários escritos sobre a linguagem Gherkin, e
por último, (Pereira et al. 2018) refere que "os profissionais devem estar cientes de que o
poder do BDD depende dos seus cenários, sendo eles base do BDD, e qualquer esforço para
facilitar sua adoção, como treino, investimento em ferramentas e assim por diante, valerá a
pena".
(Pereira et al. 2018) apenas aponta que "a maioria dos desafios associados ao BDD está
relacionada com a sua adoção, portanto, uma vez ultrapassada a curva de aprendizagem
inicial, o BDD provavelmente produzirá benefícios".
37
Capítulo 3
Esta secção é composta pela análise de valor, que tem como objetivo identificar o valor
que este estudo de caso trará ao seu publico alvo, nomeadamente a empresa e a equipa
de desenvolvimento. A análise será acompanhada pelo novo modelo de conceitos (NCD),
benefícios e sacrifícios e por último, o modelo Canvas, modelo que visa dar a conhecer a
visão global do negócio.
Segundo (Value Analysis 2005), a análise de valor é uma abordagem para melhorar o valor
de um produto ou processo, de forma a compreender os seus componentes e os seus custos
associados, e em seguida, procurar encontrar melhorias nos componentes reduzindo o seu
custo ou aumentando o valor das funções. Os conceitos chave da análise de valor são:
Valor, Função, Análise de valor e Necessidade.
• Valor: a relação entre uma função para satisfação do cliente e o custo dessa função.
• Função: o efeito produzido por um produto ou por um dos seus elementos, com o
propósito de satisfazer as necessidades do cliente.
• Análise de valor: metodologia para aumentar o valor de um produto ou processo, sendo
que este produto ou processo pode ser novo ou existente ou novo.
• Necessidade: algo que é necessário ou desejado pelo cliente.
A análise de valor identifica-se assim como um processo de análise bem definido com o
propósito de avaliar um produto ou serviço, minimizando o desperdício e outros efeitos
que não trazem valor para o produto ou serviço em questão. Segundo (Value Analysis
2005), este processo é baseado na aplicação de um plano de trabalho sistemático que
pode ser dividido em seis etapas: orientação e preparação, informação, análise, inovação e
criatividade, avaliação e implementação e monitorização.
3.2. Análise de valor 39
"Mesmo com o auxílio de processos, o resultado final pode não ser o mais desejado, sendo
que o valor é um conceito subjectivo"(Woodall 2003). A secção 3.2.2 apresenta os benefícios
e sacrifícios, que na ótica do cliente poderão ser interpretados como indicadores de valor
quando existe um equilíbrio entre ambos.
Com a introdução de um novo processo de desenvolvimento com base no TDD e BDD,
pretende-se aumentar a qualidade do software e a produtividade das equipas de trabalho.
Processos como correção de código e identificação de problemas, normalmente são processos
morosos e difíceis de realizar sem comprometer o estado atual do sistema. Com a introdução
da nova abordagem, pretendesse detetar problemas prematuramente, diminuir as falhas e
aumentar a qualidade do software produzido. Com isso, a produtividade irá aumentar de
forma natural, criando valor para a organização que despende menos recursos, clientes finais
tem acesso a mais valor de forma mais rápida e também para a equipa de trabalho, que
trabalha sobre software mais fiável e seguro.
Após a introdução ao valor na secção 3.2, segundo (Woodall 2003), o valor para o cliente
pode ser caracterizado e observado através de uma perspetiva longitudinal. Na figura 3.2
podemos observar quatro posições temporais, sendo elas Pré-compra, Ponto de negócio,
Pós-compra e por fim Após uso ou experiência.
Benefícios Sacrifícios
Pré-compra Qualidade Mudança de paradigma
Motivação Esforço
Robustez Tempo
Segurança
Ponto de negócio Adaptação Custo monetário
Motivação Tempo
Esforço
Pós-compra Qualidade
Garantia
Segurança
Após uso ou experiência Qualidade
Segurança
Eficácia
Eficiência
Maior produtividade
Após analisar a tabela 3.2, e de acordo com a definição de (Woodall 2003) sobre a perspe-
tiva longitudinal em CV, o estágio de Pré-compra contempla como benefícios a qualidade,
robustez e segurança do software e a motivação da equipa de trabalho. Como sacrifícios,
o esforço e tempo que a equipa irá despender, bem como a mudança de paradigma pro-
vocado pela nova metodologia. O estágio de Ponto de negócio apresenta como benefícios
a adaptação ao novo modelo bem como a motivação por parte da equipa. Como sacrifí-
cios, o custo monetário que será maior nesta fase, bem como o tempo e o esforço de igual
modo. O estágio de Pós-compra apresenta a qualidade, garantia e segurança do software
como benefícios. Por último, o estágio de Após uso ou experiência, como benefícios expõe
a qualidade e segurança do software e a eficiência, eficácia e maior produtividade por parte
da equipa de trabalho.
final previsto será software com mais qualidade, fiável, com a equipa a ter mais capacidade
de resposta, beneficiando todos os envolvidos, bem como os stakeholders.
A abordagem a seguir será implementada pela primeira vez na equipa de trabalho, servindo
esta dissertação como um estudo de caso, e espera-se que mais equipas adiram a esta
metodologia dentro da organização.
O modelo Canvas (Business Model Canvas) proposto por Alexander Osterwalder (Osterwal-
der e Pigneur 2010), é uma ferramenta de gestão e planeamento estratégico que tem como
propósito permitir a criação de um modelo de negocio ou melhorar um existente. Segundo
(Osterwalder e Pigneur 2011), este modelo surgiu da necessidade de criar um modelo de
negócio que todos entendam, que facilite a descrição e a discussão, para que partindo do
mesmo ponto, diferentes pessoas com diferentes conhecimentos, consigam comunicar.
O modelo Canvas é caracterizado por um diagrama de nove blocos (Osterwalder e Pigneur
2011). Na figura 3.3 podemos observar a caracterização do modelo Canvas relativamente
à proposta que é apresentada nesta dissertação.
Capítulo 4
Análise e Design
Este capítulo tem como objetivo contextualizar o estado atual dos processos, expondo os
artefactos formais e analisar soluções alternativas que visam resolver os problemas descritos
anteriormente na secção 1.2. Por fim, será apresentada a solução que melhor se adequa a
este estudo de caso.
Serão utilizados diagramas UML (Unified Modeling Language, Linguagem de modelação
unificada) de forma a expor os artefactos em questão.
Nesta secção irá ser apresentado o estado atual dos processos, intervenientes, ferramentas
e metodologias utilizadas pela equipa que irá abordar este estudo de caso.
Atualmente, a equipa que irá abordar este estudo de caso utiliza um conjunto de processos e
metodologias referidas na secção 3.1. Os papéis envolvidos no processo de desenvolvimento
podem ser observados através do diagrama de atividade presente na figura 4.1.
No diagrama 4.1 podemos então observar os papeis de Developer, Lead, Product Owner e
QA. Cada papel está associado a etapas do processo de desenvolvimento, sendo elas Todo,
48 Capítulo 4. Análise e Design
In progress, Code Review, Feature Testing, PO Approval e Ready for Merge. Na figura 4.2
podemos observar com mais detalhe o fluxo de desenvolvimento sobre estas etapas.
Como podemos observar no diagrama 4.3, a story é iniciada e os requisitos são lidos e
compreendidos por parte dos developers, após isso, o código é escrito e testado funcional-
mente pelo developer, que após assumir que a funcionalidade corresponde aos requisitos,
escreve os testes de unidade de forma a cobrir o código que foi desenvolvido, e por fim, são
desenvolvidos os testes de aceitação, caso a story o indique.
Seguindo o mesmo fluxo, na figura 4.4 podemos observar o fluxo de correção de um bug, um
erro que aconteceu de forma inesperada no sistema em produção, por parte dos developers
enquadrada na etapa de In progress.
A correção de um bug de produção tipicamente é iniciada tentando replicar o fluxo que origi-
nou o erro localmente, ou seja, na máquina do developer em ambiente de desenvolvimento.
No caso em que não é possível encontrar o erro, é feita uma réplica do estado atual de todo
4.1. Análise do estado atual 49
o sistema envolvente que originou o erro. Após replicado o cenário, o código é corrigido com
auxílio de ferramentas de debug ou logging. De seguida, o developer testa manualmente até
verificar que o bug já não acontece, recorrendo a correções de código case seja necessário.
Por fim, serão corrigidos os testes de unidade ou aceitação.
O processo de definição da story pode ser observado no diagrama 4.6. Esse processo é
designado por refinement. Antes deste evento ocorre o evento de Story Mapping, onde as
stories são criadas com um nível de detalhe ainda muito curto.
Como podemos observar no diagrama 4.6, o processo de refinement é iniciado pela explicação
da Product Owner e após ser definido o critério de aceitação, os developers discutem os
requisitos e decidem se a story tem todas as condições para avançar. Nesse caso, existe
a definição dos testes de aceitação caso seja necessário, finalizando o processo com a
atribuição de story points, unidade de medida utilizada. Após este processo concluído, a
story está pronta a ser executada. A definição dos testes de aceitação nem sempre seguem
o rigor esperado nesta fase. Para além disso, este tipo de testes são escassos, ou seja,
normalmente são definidos, em média, dois testes de aceitação para cada projeto.
As ferramentas e sistemas utilizados podem ser agrupadas por diferentes categorias: Ges-
tão de equipa, Documentação, Reporting, Desenvolvimento de software e Implantação de
4.1. Análise do estado atual 51
software. O diagrama 4.7 expõe apenas as ferramentas e sistemas que apoiam o desenvol-
vimento e implantação do software:
Analisando o processo atual, podemos evidenciar o problema ao qual este estudo de caso pre-
tende combater. Os momentos em que o problema se manifesta enquadram-se no processo
52 Capítulo 4. Análise e Design
Nesta secção serão discutidas as soluções alternativas que permitem resolver os problemas
apresentados anteriormente na secção 1.2.
O processo de desenvolvimento é chave para que sejam cumpridas metas e objetivos. Após a
análise ao problema e ao estado atual do desenvolvimento representado em 4.3, o diagrama
4.9 apresenta uma alternativa que visa combater os problemas previamente detetados nesta
dissertação.
4.2. Estudo de soluções 53
O diagrama 4.9 introduz uma nova forma de abordar o desenvolvimento das stories, baseada
na metodologia TDD, o Red-Green-Refactor. A sua premissa já foi descrita na secção 2.6.1.
Com esta nova metodologia, o fluxo de desenvolvimento altera-se de forma a realizar os
testes antes da implementação, invertendo o fluxo atual de desenvolvimento em que os
testes são realizados à posteriori. Após já não haver mais refactor para realizar, o processo
termina.
Atualmente os testes de integração não são praticados pela equipa. A proposta de introdução
serve como propósito servir necessidades onde os testes de unidade não sejam suficientes.
Os testes de integração têm como objetivo testar a integração entre módulos. Esta necessi-
dade surge quando existem stories mais técnicas. De acordo com a realidade da equipa, que
passa maioritariamente pelo desenvolvimento de APIs, este tipo de stories é mais frequente.
Perante este cenário, duas propostas são apresentadas:
• Definição do processo de refinement: Neste processo, captar a necessidade de escrita
de testes de integração, ou seja, adicionar um novo estágio ao diagrama 4.6.
• Definição durante o processo de desenvolvimento: Quando o responsável pela reali-
zação do desenvolvimento sente necessidade de complementar a validação do código
com um teste de integração.
Após analisar os benefícios do BDD, presente na secção 2.6.3, esta secção tem como objetivo
apresentar desenhos para incorporar esta prática.
Na figura 4.10 está representada uma alternativa ao processo de definição da Story. Este
diagrama foi adaptado do diagrama original 4.6. Aqui surge um novo estágio, definição de
testes de aceitação com uso do BDD. Os testes de aceitação são testes mais completos no
que diz respeito ao critério de aceitação, rigor da definição e também por conseguir ligar
várias áreas, nomeadamente desenvolvimento, negócio e clientes com a mesma linguagem.
54 Capítulo 4. Análise e Design
A figura 4.11 vem assim complementar a ideia presente no diagrama 4.10. No que diz
respeito à aplicação do BDD durante o processo de desenvolvimento, se a story referir a
necessidade de escrita de teste de aceitação, então esse teste será escrito antes do processo
de desenvolvimento (escrita de código de produção) começar.
Cucumber
SpecFlow
Seguindo a mesma linha da análise anterior, esta proposta de solução aborda o mesmo
conceito presente em 4.2.3, a utilização do BDD mas com o auxílio da ferramenta SpecFlow
e do Gherkin. Estas ferramentas podem ser executadas facilmente dentro do ecossistema
do Visual Studio.
Selenium
Como ferramenta de automação de testes de Web, o Selenium apresenta-se como uma pos-
sibilidade para os testes de aceitação. Pode ser utilizado em conjunto com o Cucumber ou
com o SpecFlow. O diagrama 4.14 representa a introdução do Selenium como uma alterna-
tiva ao conjunto de ferramentas de teste. A utilização de Selenium tem como dependência
um navegador Web para executar os testes.
Como referido em 2.3.2, a aplicação de testes de mutação é uma mais valia a avaliar a
qualidade dos testes praticados. A figura 2.3.2 apresenta a introdução deste conceito. Após
o processo de desenvolvimento estar concluído, são criados e configurados os testes de
mutação, utilizando a técnica referida em 2.3.2. Se existirem mutantes vivos, os testes
serão corrigidos, ou até mesmo o código fonte, até não existirem mutantes vivos. Após
todos os mutantes estarem "mortos", o fluxo de desenvolvimento segue naturalmente.
56 Capítulo 4. Análise e Design
AspNetCore Mvc Testing 2.2.10 tem a capacidade de criar o ambiente para testes de inte-
gração e aceitação de forma isolada (Microsoft 2019c), permitindo preparar todo o setup
inicial de acordo com o teste em questão (Microsoft 2019b). Na figura 4.16 podemos ob-
servar a interação entre componentes, onde o componente Tests requer uma nova instância
ao componente AspNetCore Mvc Testing, que por sua vez cria e devolve uma nova instância
da aplicação alvo Application.
MockServer
O MockServer é uma ferramenta que corre na máquina onde os testes são executados.
Depende da JVM para executar. Está fora do ecossistema do Visual Studio, ou seja, funciona
como um processo externo na máquina em questão.
4.2. Estudo de soluções 57
WireMock
O WireMock 2.2.8 apresenta-se como uma ferramenta que está dentro do ecossistema da
solução, que por sua vez dentro do Visual Studio. Depende do .NET Framework.
Na figura 4.18 podemos observar a interação entre componentes.
Mongo2Go
O Mongo2Go 2.2.9 é uma ferramenta que oferece uma simulação da base de dados Mon-
goDB. A vantagem da utilização deste componente é o facto de ficar encapsulado à solução,
e por sua vez ao Visual Studio, ou seja, é uma dependência que executa sobre um processo
interno e não externo à execução da solução. Oferece rapidez e facilidade de desenvolvi-
mento, dando a possibilidade de criar várias instâncias de base de dados em simultâneo.
Na figura 4.19 podemos observar a interação entre os principais componentes, sendo eles o
componente de Tests, que pode representar um teste de unidade, integração ou aceitação,
que por sua vez comunica com a Application Instance, instância da aplicação alvo a ser
testada que naturalmente comunica com o componente de base de dados, neste caso o
Mongo2Go.
Docker
O Docker 2.4.3 apesar de apresentar muitas capacidades, neste contexto apresenta-se como
uma alternativa à virtualização da base de dados, permitindo assim criar uma imagem da
58 Capítulo 4. Análise e Design
base de dados alvo, manipular e apagar a mesma. Como pressuposto, este processo teria
de executar lado a lado com a bateria de testes, e estaria fora do ecossistema da solução.
Na figura 4.20 podemos observar a interação entre componentes, sendo o ponto de destaque
a comunicação entre a Application Instance e o Docker, que está fora do ecossistema do
Visual Studio. O Docker utiliza assim um DockerImage que contém uma instância do
MongoDB.
Nesta secção irá ser descrita a escolha da solução que irá ser utilizada neste estudo de caso.
Para fundamentar a escolha, esta será comparada com as soluções apresentadas previamente
na secção 4.2. Diagramas de UML serão apresentados de modo a fundamentar com base
no contexto atual o que irá ser alterado para implementar a solução escolhida.
Após uma análise ao problema e às soluções desenhadas, a solução a adotar passa pela
introdução do Test-First, TDD e do BDD. Após uma análise ao problema 1.2 e aos objetivos
desta dissertação 1.3 vários artigos foram analisados com o intuito de perceber qual o
caminho a seguir, e a escolha do Test-First, TDD e BDD pode ser fundamentada por
algumas conclusões retiradas de casos de estudo previamente realizados.
Segundo (Nagappan et al. 2008) e (Janzen e Saiedian 2006), Test-First não é apenas uma
técnica de teste. O TDD é considerado um processo de design e, em alguns casos, mais
que um processo de teste. A granularidade fina do ciclo de teste e código fornece feed-
back contínuo, sendo que as falhas ou defeitos são identificados muito cedo. As Breaking
change são detetadas cedo. (Janzen e Saiedian 2008) defende que o TDD produz código
mais simples, mais coeso e menos acoplado do que o código desenvolvido sobre o processo
tradicional, Test-Last. (Jeffries e Melnik 2007) refere ainda que, o TDD é uma disciplina
de design, não uma atividade de testes. Qualquer pessoa que tenha trabalhado em software
legacy reconhece a situação em que um sistema continua a funcionar, mas cada vez mais
se torna desatualizado até que se transforma num castelo de cartas. Ninguém quer tocar,
porque mesmo uma pequena alteração no código provavelmente levará a um efeito colateral
indesejado. O TDD não é silver bullet, no entanto pode ajudar a formar developers mais
disciplinados e efetivos. Segundo (Solis e Wang 2011) e (Introducing BDD 2006), O BDD
é uma evolução do TDD e ATDD, sendo benéfico a utilização do TDD e BDD em conjunto
para que os requisitos sejam cumpridos e interpretados da melhor forma.
Após esta análise, e de acordo com as ambições da organização e da equipa de traba-
lho, o caminho a seguir fica traçado. Os artefactos seguintes exemplificam como será a
arquitectura da solução e das ferramentas a utilizar.
4.3. Escolha da solução 59
Após uma análise à alternativa 4.2.1, a solução aí proposta apesar de válida, é incompleta
para este estudo de caso. Segundo alguns autores como (Introducing BDD 2006) e (Solis
e Wang 2011), o TDD deve ser complementado com o BDD. O que o diagrama propõe
é complementar a alternativa proposta com a introdução do BDD 4.11, ou seja, a escrita
do(s) teste(s) de aceitação antes de qualquer escrita de código, no caso de a story refira
essa necessidade. Seguindo a mesma abordagem do Red-Green-Refactor, o fluxo de de-
senvolvimento seguirá dessa forma. As vantagens desta abordagem já foram referidas na
secção 2.6.3 e 2.6.1.
Segundo os princípios do TDD e BDD, o processo de correção passa por escrever o teste
que reporta esse bug, e de seguida é feita a correção do código até o(s) teste(s) passar(em).
Após a barra verde aparecer, o processo de refactor entra em cena, seguindo a mesma lógica
referida anteriormente Red-Green-Refactor.
O diagrama de sequência 4.24 fundamenta e exemplifica como um developer procede na
correção de um bug, de acordo com a proposta presente em 4.23. O componente Test pode
representar um teste de unidade, integração ou aceitação. Com este método, garantimos que
o mesmo erro nunca mais acontece, aumentando a confiança no código e a sua cobertura.
Para além dos componentes presentes em 4.25, o diagrama apresenta também a interac-
ção entre eles. Podemos assim evidenciar dois dois grandes componentes, Visual Studio e
Solution. O Visual Studio integra o Plug-In LiveUnitTestes 2.2.3, ferramenta que dá res-
posta em tempo real do resultados dos testes e da cobertura dos mesmos sobre os código
fonte. A Solution apresenta os ApplicationProjects que naturalmente dependem de .NET
Framework, ferramenta utilizada pela empresa e TestsProjects, que estão ligados a uma
série de componentes. Após uma análise às propostas apresentadas na secção 4.2, este
diagrama reflete a escolha de cada componente.
A escolha xUnit é óbvia, pois é a ferramenta já utilizada pela equipa e aquela que oferece
todas as condições para a realização de todo o tipo de testes de forma fiável.
62 Capítulo 4. Análise e Design
Capítulo 5
Implementação
Neste capítulo serão abordados os conceitos utilizados para implementar, de acordo com a
solução desenhada no capítulo 4, a solução final.
5.1 Enquadramento
A seguinte lista representa os principais tópicos que serão abordados para expor todo o
trabalho realizado durante a execução desta dissertação.
• Contexto do projeto: Ambiente e enquadramento em que este estudo de caso se
realizou.
• Instanciação do projeto: Estágios que fizeram parte deste estudo de caso e exempli-
ficação de como foram executados.
• Treino sobre a equipa de desenvolvimento: Treino realizado com o intuito de formar e
introduzir os conceitos chave para a realização da proposta desenhada na secção 4.3.
• Test-Driven Development: Apresentação das metodologias introduzidas, métodos e
técnicas de auxilio a esta prática.
• Behavior-Driven Development: Introdução, enquadramento e aplicação desta meto-
dologia.
• Solução de testes: Desenho e implementação da bateria de testes montada sobre a
solução.
• Integração com CI/CD Pipeline: Integração da bateria de testes desenvolvida com a
Pipeline de CI/CD.
O projeto em que se realizou este estudo de caso foi um projeto iniciado do zero. O
projeto passou pelo processo de setup (configuração) inicial após a sua criação, adotando as
normas arquitecturais recomendadas, que passam por preparar a infraestrutura da aplicação
para seguir a estrutura comum existente e separar em diferentes camadas os componentes
que compõe a aplicação. Após o setup estar concluído, novos desenvolvimentos começaram
a ser desenvolvidos.
66 Capítulo 5. Implementação
O projeto em questão é um serviço, mais precisamente uma Rest API, desenvolvida sobre
a tecnologia ASP.NET Core 2, na linguagem C#, não contendo nenhuma interface gráfica.
"ASP.NET Core suporta a criação de serviços RESTful, também conhecidos como Web
APIs, usando C#. Para lidar com solicitações, uma web API usa controladores"(Microsoft
2019a).
A figura 5.1 apresenta o diagrama de alto nível da arquitectura dos componentes. O com-
ponente clients representam os consumidores da Rest API, que é composta pelos seguintes
componentes:
• HTTP Pipeline: Responsável por gerir os pedidos HTTP.
• WEB API Controllers: Componente que expõe os contratos e endpoints (ponto de
entrada) que a aplicação aceita.
• Service Layer : Camada de serviços da aplicação.
• Data Layer : Camada de gestão interna.
• Repository Layer : Camada de persistência.
O IDE utilizado foi o Visual Studio. O projeto estava ligado ao sistema de controlo de versões
e a uma pipeline de CI/CD. A pipeline era responsável por criar uma imagem da aplicação,
executar toda a bateria de testes (de unidade, integração e aceitação), executar passos
internos definidos pela organização e por último, implantar nos servidores de produção.
A solução desenhada foi apresentada à equipa de trabalho em várias sessões, onde foi possível
discutir e analisar a solução proposta e receber feedback adicional dos diferentes membros
com sugestões, de forma a criar um ambiente contributivo e construtivo em prol de melhorar
os processos atuais. Como a nova solução desenhada obriga a uma mudança de paradigma de
programação e trabalho, foi dada a abertura a todos os membros da equipa de utilizarem ou
não a nova abordagem, criando assim um ambiente com menos atrito e rigidez. A estratégia
passou por incentivar os membros a utilizarem a solução desenhada, recorrendo a estudos
científicos realizados a nível industrial com demonstração de resultados e também, em forma
de alerta, expor os problemas identificados que persistem no processo de desenvolvimento
anterior à nova proposta.
5.3. Instanciação do processo 67
A instanciação do processo passou por, numa primeira fase, pesquisar informação relevante
sobre as técnicas que iriam ser introduzidas à equipa. Após esse processo, essa informação
foi adaptada e enquadrada à realidade da empresa e da equipa para que de uma forma
paralela, dois processos ocorressem: o treino sobre a equipa e o desenvolvimento do projeto.
O diagrama de atividades 5.2 exemplifica a instanciação do processo.
Tal como referido na secção 5.2, a proposta desta dissertação passa por alterar o processo
de desenvolvimento atual, e para isso foram agendadas reuniões recorrentes de forma a
introduzir os principais temas e treinar a equipa para que adquira todo o conhecimento
necessário para executar a proposta detalhada na secção 4.3.
O treino exercido sobre a equipa abordou os seguintes tópicos:
• Apresentação do problema a que esta dissertação se compromete a resolver.
• Apresentação da possível solução para o problema existente.
• Introdução ao Test-First, TDD e BDD, práticas chave para executar a solução dese-
nhada.
• Resultados esperados.
A técnica utilizada para realizar as formações e treino foi o Coding dojo, um encontro em que
vários developers se reúnem para trabalhar sobre um desafio de programação. O objetivo
é aprender, ensinar e melhorar com outros developers sobre um ambiente não competitivo,
num curto intervalo de tempo (1 a 1,5 horas) (Wright 2009).
Todas as práticas referidas foram acompanhadas com sessões de treino específico, com uti-
lização de exemplos reais que a equipa lida habitualmente no seu dia a dia de trabalho. Para
complementar estas sessões, existiram e existem ocasionalmente sessões de esclarecimento
e acompanhamento quando surgem dúvidas ou questões sobre qualquer tema introduzido.
Segundo (Shull et al. 2010), é recomendável abordar o TDD associando um developer inex-
periente com alguém mais experiente. Esta prática irá ajudar no processo de interiorização
do TDD, enquadrado no processo de desenvolvimento de software.
68 Capítulo 5. Implementação
O diagrama temporal 5.3 mostra com mais detalhe o momento no tempo onde os temas
foram introduzidos à equipa.
O diagrama 5.3 mostra que no primeiro mês (janeiro) foi realizada a introdução do problema
e a proposta de resolução, sendo que seguidamente foram introduzidos de forma contínua
as técnicas TDD e BDD em simultâneo com sessões de treino.
• Caraterísticas dos testes: Para que o TDD seja aplicado e utilizado na sua íntegra de
forma regular, a bateria dos testes da solução deve obedecer a um conjunto de cara-
terísticas, de forma a que seja possível executar todos os testes o mais rápido possível
para que o feedback seja imediato mediante novos desenvolvimentos de código. Para
isto ser possível, várias abordagens foram estudadas e aplicadas de forma a garantir
rapidez e eficiência no que diz respeito aos testes, tal como referido na tabela 2.1. Na
secção 5.7.2, irão ser apresentadas as caraterísticas dos testes com exemplos.
• Padrões (testes): Os padrões introduzidos sobre TDD foram baseados no livro publi-
cado por (Beck 2003). O objetivo destes padrões é facilitar e viabilizar a implemen-
tação dos testes utilizando técnicas que para Beck são essenciais ao aplicar o TDD.
O conjunto de padrões Test n, Isolated Test, Test List, Test-First, Assert First, Test
Data e Evident Data compõem a lista que foi introduzida na equipa.
– Test n: Devemos escrever um teste automatizado sempre que existam mudanças
no sistema, de forma a garantir que o comportamento será mantido, e que as
novas alterações não comprometeram o estado do sistema.
– Isolated Test: A execução de testes deve ser completamente isolada, ou seja,
cada teste não pode afetar nenhum outro teste. Beck defende que os testes
devem ser rápidos de executar, permitindo uma análise contínua ao resultado de
todos os testes e com isso garantir que nada está errado, e que o resultado de
um teste é independente do resultado dos restantes, garantindo que o problema
é detetado de forma clara e precisa.
– Test List: Deve ser feita uma lista de testes que à priori terão de ser escritos
antes de começar a escrever código. O objetivo é perceber exatamente aquilo
que queremos realizar, pensando antes de executar, colocando na lista os cenários
que temos em mente quando pensamos no problema. À medida que os testes
são executados, podem surgir novas necessidades de acrescentar testes, e nesse
caso, adicionamos à lista. "A primeira parte de nossa estratégia para lidar com
o stress da programação é nunca dar um passo adiante, a menos que saibamos
onde o nosso pé vai pousar."(Beck 2003)
– Test-First: Devemos testar primeiro, antes de escrever o nosso código. Existe
a necessidade de pensar no design e ter um método para controlar o compor-
tamento da nossa funcionalidade, ao desenhar o teste primeiro torna-nos mais
propícios a testar e assim não ignoramos este passo.
– Assert First: Devemos escrever as afirmações do teste primeiro. Beck defende
esta prática como sendo um processo simplificador e esclarecedor.
– Test Data: Os dados de entrada para os testes devem ser fáceis de ler e seguir.
Exceto os testes que precisam de entradas precisas com determinados dados,
as entradas devem ser combináveis. Quando um teste contém um cenário com
várias entradas talvez estamos presentes a um caso onde devemos repensar o
nosso design e implementação.
– Evident Data: Devemos incluir resultados esperados e reais no teste, criando uma
relação entre os dados de entrada e o resultado esperado. O objetivo passa por
escrever o teste de forma clara para que o próximo leitor consiga compreender
de forma mais clara o cenário, os dados de entrada e o resultado esperado.
70 Capítulo 5. Implementação
externos. O exemplo clássico é um teste que execute uma unidade que dependa
de uma base de dados, que levam muito tempo para serem iniciados, são difíceis
de manter limpos. Com o uso de Mock Object, esta dependência é facilmente
criada sem comprometer a velocidade do teste.
– Log String: Técnica utilizada para testar uma sequência de chamadas. Para isso,
pode ser utilizado um log que é escrito a cada chamada, contendo a informação
da sequência.
– Crash Test Dummy : Utilização de um objeto especial que lança uma exceção,
utilizado para replicar um cenário de erro e verificar o comportamento do sistema.
Esta técnica ajuda no sentido em que não é necessário replicar um ambiente real
para simular este comportamento.
– Broken Test: Técnica utilizada quando se está a programar sozinho, onde o
último teste deve falhar, para que quando o executante retomar a sessão de
programação se lembre exatamente no ponto onde estava.
– Clean Check-in: Consiste em sair de um sessão de programação em equipa e
deixar todos os testes a executar com sucesso, de forma a não comprometer o
trabalho futuro do resto da equipa.
• Refactor : No TDD, refactoring é um passo muito presente e utilizado, pois é o ultimo
passo do ciclo Red, Green, Refactor. As técnicas de refactor apresentadas à equipa
foram: Reconcile Differences, Isolate Change, Migrate Data, Extract Method, Inline
Method, Extract Interface, Move Method, Method Object, Add Parameter e Method
Parameter to Constructor Parameter. Todos estes conceitos foram baseado no livro
de (Beck 2003), onde refere que "o refactor não pode alterar o comportamento do
programa sob nenhuma circunstância". Estes princípios são tão ou mais importantes
que todos os conceitos já mencionados, pois é importante para a equipa perceber
e saber como proceder ao refactor de forma segura sem alterar o comportamento
aplicacional.
– Reconcile Differences: Unificar duas partes do código semelhantes, eliminando a
duplicação.
– Isolate Change: Isolar determinada parte do código que precisa de ser alterada
antes de proceder à alteração.
– Migrate Data: Técnica que consiste em duplicar temporariamente o componente
que se pretende migrar, contendo assim uma parte interna e externa. Na parte
interna será alterada a representação e, em seguida, será alterada a interface
visível.
– Extract Method: Processo de criação de um novo método simples quando esta-
mos perante um método longo e difícil de ler. Após a criação do novo método,
o método original terá de o invocar, mantendo o comportamento da unidade.
– Extract Interface: Criar uma interface com base nas operações de uma classe.
– Move Method: Mover um método para a classe corresponde e em seguida, rela-
cionar todas as invocações à nova localização.
72 Capítulo 5. Implementação
O BDD, tal como o TDD 5.5, foi introduzido e implementado sobre a equipa de desenvolvi-
mento gradualmente ao longo do tempo. A equipa relativamente ao BDD já tinha um certo
conhecimento desta prática, pois já tinha experienciado antes de uma forma superficial, o
que facilitou toda a introdução e aplicação. Perante este cenário, várias apresentações foram
realizadas de forma a introduzir os seguintes temas:
• Introdução ao BDD: A introdução teórica deste tema teve como base o seu autor
(Introducing BDD 2006). As suas motivações e qual é o principal problema a que o
BDD se compromete a resolver, com base em exemplos que o próprio autor refere em
(Introducing BDD 2006), serviram para consolidar as bases que a equipa já tinha, e
fundamentar e discutir alguns conceitos que esta prática incorpora, como o Gherkin.
• Boas práticas: As boas práticas introduzidas serviram de apoio ao desenvolvimento
saudável e escalável da solução de testes de aceitação. Baseadas em (Adams 2007)
e (Introducing BDD 2006), o seguinte conjunto de boas práticas foi introduzida à
equipa: Behaviour Focus, Ubiquitous Language, Declarative Features, Avoid Conjunc-
tive Steps, Reuse Step Definitions e Make Scenarios Atomic.
– Behaviour Focus: Deve existir foco no comportamento, de modo a definir cená-
rios reais aos que o utilizador final irá utilizar.
– Ubiquitous Language: A linguagem deve ser omnipresente, simples e transversal.
– Declarative Features: Os cenários devem ser escritos como que se um utilizador
os descrevesse.
5.7. Solução de testes 73
– Avoid Conjunctive Steps: Evitar criar ações conjuntas, ou seja, ações compostas
com um "e"(and), por exemplo “Given I am on the homepage and scrolled page
down”. Perante este cenário, a ação deve ser dividida, ou seja, separar em dois
steps diferentes.
– Reuse Step Definitions: Devem ser reutilizadas as etapas (steps), de modo a
melhorar a capacidade de manutenção, como por exemplo, quando é necessário
alterar um determinado comportamento, basta alterar sobre uma etapa única.
– Make Scenarios Atomic: O cenário deve ser executado independentemente, sem
dependências de outros cenários. Isso ajuda o processo de depuração na percep-
ção de erros.
• Introdução ao SpecFlow : Foram realizadas sessões de experimento com BDD e o uso
da framework SpecFlow, de forma a familiarizar e ajudar os membros a utilizarem esta
ferramenta, diminuindo a curva de aprendizagem inicial.
Relativamente aos desafios/limitações do uso do BDD, a seguinte enumeração irá apresentar
os diferentes motivos:
• Utilização do SpecFlow3 : A implementação do SpecFlow3 na solução foi difícil, pois
a framework apesar de ser compatível com o tipo de projeto ASP.NET Core 2, requer
algumas configurações não triviais para evitar erros inesperados, erros de compilação,
etc. Após a configuração ficar concluída, a equipa não experenciou nenhum problema
adicional, sendo que podemos concluir que apenas na fase inicial é que os problemas
surgiram.
• Definição de cenários: A definição de cenários não foi tão trivial como esperado,
pois a maioria dos exemplos documentados em artigos remetem para sistemas onde
normalmente apresentam interface gráfica, e o processo de visualização surge como
um forte fator de ajuda na definição dos cenários. Neste caso, como o projeto é uma
API, a adaptação foi um pouco mais lenta, mas após o paradigma de pensamento
estar alinhado, os cenários fluíram de forma muito natural.
• Feedback do cliente: Infelizmente a equipa não conseguiu experenciar o contacto com
o cliente, de forma a o envolver no processo, quer na definição de cenários como no
seu feedback.
A solução de testes (ou bateria de testes) foi desenvolvida e pensada de acordo com muitos
dos princípios defendidos por autores como Kent Beck (Beck 2003), Martin Fowler (Fowler
2014), Dan North (Introducing BDD 2006) e (Cheon e Leavens 2002). A seguinte lista
detalha as principais caraterísticas da solução de testes implementada:
• Segregação: A solução de testes está dividia em camadas, respeitando a pirâmide de
testes (Luo 2001), testes de unidade, integração e aceitação.
• Isolamento: Todas as camadas estão isoladas de forma independente, e dentro de
cada camada, cada teste é completamente isolado.
• Rapidez: A solução de testes executa num curto intervalo de tempo, dando feedback
recorrente às alterações introduzidas.
74 Capítulo 5. Implementação
• Escalabilidade: Permite que sejam desenvolvidos novos testes de forma rápida e segura,
graças à sua infraestrutura.
• Encapsulamento: A solução de testes encapsula todas as suas dependências e tem
autonomia para executar em qualquer ambiente de forma isolada.
• Paralelização: Todos os testes, independentemente da camada a que pertencem, po-
dem ser executados de forma paralela em simultâneo.
A figura 5.4 apresenta a atual estrutura da solução de testes. Dentro da secção Tests
podemos observar três diferentes secções: Unit (Testes de unidade), Integration (Testes de
integração) e End-2-End (Testes de aceitação). Esta estrutura tem como base a pirâmide
de testes 2.1, segundo (Luo 2001).
5.7.1 Componentes
Os componentes utilizados para estruturar a solução de testes foram escolhidos com o intuito
de satisfazer os requisitos afirmados em 5.7, sempre com o objetivo de facilitar o desen-
volvimento e tornar a suite de testes amigável. A seguinte lista apresenta os componentes
utilizados na solução de testes:
• Moq: Esta framework serve essencialmente para utilizar mocks de forma simples e rá-
pida. Apresenta uma performance excelente, não impactando a velocidade dos testes.
5.7. Solução de testes 75
Permite simular comportamentos e verificar ações realizadas. "A sua API é extrema-
mente simples e direta, e não requer nenhum conhecimento prévio ou experiência com
conceitos de Mock"(Moq 2019).
• FluentAssertions: Permite que de forma natural e simples se definam resultados es-
perados da execução dos testes, facilitando a sua escrita e o seu assert. Segundo
(FluentAssertions 2019), esta framework é "um conjunto muito extenso de métodos
que permitem especificar de forma natural o resultado esperado de um teste sobre o
estilo TDD ou BDD".
• xUnit: Framework apresentada em 2.2.1, foi utilizada de forma recorrente em todas
as camadas de testes.
• Live-Unit-Tests: Plug-in que se apresenta como uma peça chave no que diz respeito
ao feedback contínuo 2.2.3.
• WireMock: Referido em 2.2.8, WireMock com a sua versatilidade permitiu simular
chamadas externas da aplicação, isolar os testes de forma independente e como con-
sequente, permitir a sua paralelização.
• Mongo2Go: Componente capaz de criar e fornecer bases de dados MongoDB tem-
porários e isolados para testes de unidade ou integração (Mongo2Go 2019b), o que
permite mais uma vez criar ambientes isolados para cada teste.
• Microsoft.AspNetCore.Mvc.Testing: Ferramenta utilizada para criar o ambiente para
testes de integração e aceitação de forma isolada (Microsoft 2019c), permitindo pre-
parar todo o setup inicial de acordo com o teste em questão (Microsoft 2019b) .
• SpecFlow 3 : Ferramenta 2.2.6 utilizada para testes de aceitação.
Ao longo das secções 5.7.2, 5.7.3 e 5.7.4 os componentes referidos serão evidenciados e
exemplificados de forma a sinalizar a sua utilização em cada camada de testes.
Os testes de unidade ocupam a maior percentagem de testes que compõe a solução. Para
uma melhor compreensão de como o processo é realizado na realização de um teste de
unidade, o diagrama 5.6 apresenta a sequência de passos que o developer executa.
Tal como exemplifica a figura 5.6, o developer inicia o processo de escrita de um teste de
unidade seguindo a anotação dos três As 2.1, de seguida e de forma opcional, pode escrever
o Assert (Asserto) do teste, prática do TDD Assert First. Após isso, o teste deve falhar
e a escrita do corpo do mesmo deve prosseguir, seguindo os padrões TDD referidos em
5.5. Após o teste concluído, a escrita do código de produção deve ser realizada até que o
todos os testes passem. O último passo é o processo de refactor, que serve para eliminar
a duplicação e limpar o código, estando este processo terminado quando todos os testes
passarem.
O diagrama 5.7 apresenta os componentes utilizados na soluções de testes de unidade, sendo
eles o FluentAssertions, Moq, Live-Unit-Testing e xUnit.
O excerto 5.1 mostra um exemplo de um teste de unidade. O teste utiliza a norma dos
três As 2.1 e o próprio nome enfatiza aquilo que (Reese, A. D. George e Wenzel 2018)
76 Capítulo 5. Implementação
5 v a r u s e r = new U s e r
6 {
7 E m a i l = " l o r e m @ e m a i l . com" ,
8 Name = " Lorem I p s u m " ,
9 UserName = " l o r e m . i p s u m " ,
10 PhoneNumber = " 3290082 "
11 };
12
13 v a r e x p e c t e d = new D i c t i o n a r y < s t r i n g , s t r i n g >
14 {
15 { " F i r s t N a m e " , " Lorem " } ,
16 { " LastName " , " I p s u m " } ,
17 { " P e r s o n E m a i l " , " l o r e m @ e m a i l . com" } ,
18 { " PhoneNumber " , " 3290082 " }
19 };
20
21 // Act
22 var a c t u a l = t h i s . sut . Convert ( user ) ;
23
24 // A s s e r t
25 e x p e c t e d . S h o u l d ( ) . Be ( a c t u a l ) ;
26 }
Os testes de integração surgem como peça chave no que diz respeito à validação com
dependências externas da aplicação, testes de contrato e comunicação com componentes
internos. O diagrama 5.8 apresenta os componentes utilizados na solução de testes de
integração. Para além dos referidos em 5.7.2, surge o Mongo2Go, MvcTesting e WireMock.
O diagrama de sequência 5.9 exemplifica como um developer cria um novo teste de integra-
ção, utilizando os componentes presentes em 5.8. Seguindo a mesma linha de pensamento
aplicada nos testes de unidade, relativamente ao padrão TDD Assert first, neste caso pode
ser também aplicado de forma opcional. A instanciação dos componentes é muito sim-
ples, permitindo a que o developer construa rapidamente um ambiente à imagem da sua
necessidade. De forma a complementar este diagrama, alguns excertos de código irão ser
apresentados.
O excerto 5.3 apresenta um exemplo de um teste de integração, que à semelhança dos
testes de unidade, segue a estrutura dos três As 2.1. No excerto, podemos observar os
seguintes comportamentos:
• Criação do FluentMockServer, instância da ferramenta do WireMock. O teste pode
usufruir dessa instância isolada, pois contém uma porta única. O teste é responsável
também por enviar a simulação das chamadas que a aplicação irá fazer para o exterior
(server.StubRequest(...)), que por sua vez o WireMock irá intercetar e responder com
o que foi configurado nessa simulação.
• Um novo client, através do WebApplicationFactory que utiliza o MVCTesting 5.7.1. O
teste utiliza uma nova instância da aplicação alvo, que pode utilizar de forma isolada.
• Criação de um pedido HTTP para a aplicação alvo Endpoint, com um input previa-
mente definido.
• A validação do resultado
• Por fim, as instâncias FluentMockServer e client serão destruídas.
5.7. Solução de testes 79
1 [ Fact ]
2 p u b l i c a s y n c Task U s e r s _ U s e r E v e n t _ P o s t U s e r ( )
3 {
4 // A r r a n g e
5 var expected = 201;
6 v a r i n p u t = new U s e r E v e n t ( ) ;
7
8 using ( var s e r v e r = FluentMockServer . Start () )
9 {
10 s e r v e r . S t u b R e q u e s t ( new U s e r R e q u e s t ( ) ) ;
11
12 u s i n g ( v a r c l i e n t = new W e b A p p l i c a t i o n F a c t o r y ( ) . B u i l d ( ) .
WithMockServer ( ) )
13 {
14 // Act
15 var r e s p o n s e = await c l i e n t . PostAsync ( " Users " , i n p u t ) ;
16
17 // A s s e r t
18 A s s e r t . True ( r e s p o n s e . S t a t u s C o d e ( ) , e x p e c t e d ) ;
19 }
20 }
21 }
O teste de integração 5.4 mostra um exemplo do uso do Mongo2Go, utilizado para criar uma
base de dados MongoDB para o efeito de teste. A sua utilização é semelhante aos outros
componentes mencionados, o MongoServer é responsável por criar uma nova instância da
base de dados utilizando uma determinada conexão (ConnectionString), que por sua vez a
instância da aplicação alvo irá utilizar. Após o Assert, a instância é destruída tal como todas
as dependências criadas.
1 [ Fact ]
2 p u b l i c a s y n c Task U s e r _ U s e r E v e n t _ S a v e U s e r I n t o D B ( )
3 {
4 // A r r a n g e
80 Capítulo 5. Implementação
Os testes de aceitação (ou End-2-End) tem um papel muito importante na validação dos
requisitos da aplicação. O diagrama de sequência 5.10 apresenta os passos que o developer
executa na escrita deste tipo de testes. O developer após a criação do teste de aceitação,
escreve a notação BDD utilizando Gherkin, os Step definitions (passos do teste) que repre-
sentam cada linha do texto escrito previamente em Gherkin. Seguindo o mesmo princípio
do TDD, o teste deve falhar, e de seguida o developer é responsável por escrever o código
de produção até que o teste passe.
O excerto de código 5.6 mostra a correspondência dos passos definidos em 5.5. O SpecFlow
ajuda no processo de criação e correlação entre o Gherkin e o código correspondente. Estes
passos podem ser reutilizáveis de modo a reduzir redundância e duplicação de código.
1 [ Binding ]
2 public c l a s s UserTestsSteps
3 {
4 p r i v a t e EventType @event ;
82 Capítulo 5. Implementação
5 p r i v a t e HttpResponseMessage r e s u l t ;
6
7 ...
8
9 [ G i v e n (@"A ’ ( . ∗ ) ’ u s e r e v e n t " ) ]
10 p u b l i c v o i d GivenAUserEvent ( EventType eventType )
11 {
12 t h i s . @event = eventType ;
13 }
14
15 [ When (@" I c a l l t h e ’ ( . ∗ ) ’ r o u t e " ) ]
16 p u b l i c v o i d WhenICallTheRoute ( s t r i n g r o u t e )
17 {
18 u s i n g ( v a r d B s e r v e r = MongoServer . Create ( c o n n e c t i o n S t r i n g ) )
19 using ( var s e r v e r = FluentMockServer . Start () )
20 u s i n g ( v a r c l i e n t = new W e b A p p l i c a t i o n F a c t o r y ( )
21 . Build ()
22 . WithMockServer ( )
23 . WithMongoDb ( c o n n e c t i o n S t r i n g )
24 . Create () )
25 {
26 t h i s . r e s u l t = a w a i t t h i s . C a l l R o u t e ( t h i s . @event , r o u t e ) ;
27 }
28 }
29
30 [ Then (@" t h e u s e r s h o u l d be ’ ( . ∗ ) ’ " ) ]
31 p u b l i c v o i d ThenTheUserShouldBe ( ActionType a c t i o n T y p e )
32 {
33 var a c t u a l = t h i s . V e r i f y A c t i o n ( actionType , t h i s . r e s u l t ) ;
34 Assert . IsTrue ( actual , t h i s . GetErrorMessage ( actionType ) ) ;
35 }
36 }
Um dos desafios desta implementação foi perceber como e qual a melhor forma de guardar o
contexto entre step definitions. Como o contexto não pode ser guardado no navegador web
(como acontece em aplicações gráficas), o contexto tem de ser gerido internamente e após
a conclusão do teste, tem de ser destruído. No excerto 5.6 podemos observar a existência de
duas variáveis privadas de classe que são utilizadas ao longo da execução dos step definitions,
nomeadamente o @event que representa o tipo de evento a considerar (request, creation
e delete) e result, que representa o resultado da operação efectuada. Após a instanciação
dos step definitions, a classe UserTestsSteps guarda em memória o valor das variáveis, e à
medida que a execução dos step definitions é realizada, o valor das variáveis é partilhado,
de modo a ser utilizado no respetivo step.
A solução desenvolvida pela equipa está integrada sobre uma Pipeline de CI/CD, e para
além disso, a bateria de testes foi também adicionada a esta pipeline de modo a executar
todas as camadas de teste de forma automática, garantido que todos os passos de validação
foram executados com sucesso antes de qualquer implantação.
5.8. Integração com CI/CD Pipeline 83
A estratégia utilizada foi segmentar as diferentes camadas de testes (de unidade, integra-
ção e aceitação) em diferentes stages (estágios) da Pipeline, de modo a que seja possível
identificar facilmente qual camada de testes falhou, estando cada stage responsável por
executar determinada camada de testes. A figura 5.12 representa uma versão simplificada
da Pipeline executada no Jenkins 2.4.2, com os diferentes stages responsáveis por executar
os diferentes tipos de testes.
Após a execução do stage build, os testes de unidade UnitTests são executados e seguida-
mente, após um Deploy sobre um ambiente de desenvolvimento isolado, são executados os
testes de integração IntegrationTests. Os teste de aceitação AcceptanceTests são execu-
tados sobre uma réplica do ambiente de desenvolviemnto. Após serem executados todos os
outros stages (sinalizados com "...") é feita a implantação nos servidores de produção.
85
Capítulo 6
Avaliação
6.1 Hipótese
A definição das grandezas tem como propósito ajudar a avaliar o resultado deste estudo de
caso. A sequência seguinte representa o conjunto de grandezas que serão utilizadas:
• Satisfação da equipa: o objetivo é analisar se a equipa se sente confortável com a
nova solução
• Adoção da nova solução: analisar se a equipa usou de forma contínua a solução
desenhada.
• Produtividade da equipa: o objetivo é analisar se a produtividade da equipa aumentou
ou diminuiu com a implementação da nova solução.
• Defeitos no código: esta grandeza irá medir o número de defeitos no código em
produção. Será um forte indicador para perceber a eficácia desta nova solução.
• Cobertura do código: a cobertura do código servirá para medir a percentagem de
código coberto pela bateria de testes.
• Qualidade dos testes: indicador importante no que diz respeito à qualidade da bateria
de testes, refletindo o quão forte é a nossa bateria de testes e a confiança que podemos
ter sobre ela.
86 Capítulo 6. Avaliação
Após definidas as grandezas, as hipóteses presentes nesta secção visam definir o que se
pretende efetivamente avaliar.
• Aumento da satisfação da equipa: É expectável que a equipa se sinta satisfeita com
a nova solução, sendo que a percentagem de satisfação deve ser superior a 90%.
• Adoção continua da nova solução: É esperado que a equipa adote a solução de forma
natural e de forma constante, e por fim, a considere eficaz e adequada ao problema.
A percentagem de utilização deverá ser superior a 80%.
• Aumento da produtividade da equipa: É esperado que a produtividade da equipa dimi-
nua nos primeiros tempos de introdução da nova metodologia, mas que aumente ao
longo do tempo. É esperado que este valor aumente relativamente ao modelo base.
• Diminuição de defeitos em produção: É expectável uma diminuição do número de
defeitos (bugs) em código de produção com a introdução da nova solução.
• Aumento da cobertura do código: Atualmente a cobertura do código é de cerca de
(60%) em todas as soluções de software que a equipa trabalha. É esperado um
aumento entre 20% a 30%.
• Aumento da qualidade dos testes: A percentagem de mutantes deve ser inferior a 5%,
ou seja, é esperado que os testes aumentem a sua qualidade, para a bateria de testes
fique mais forte.
• Diminuição do tempo da bateria de testes: É esperada uma redução do tempo da
bateria de testes, que incluem todo o tipo de testes (unidade, integração e aceitação).
Este valor deve ser inferior a 100 segundos.
Esta secção tem como objetivo apresentar os diferentes métodos de avaliação, nomeada-
mente os inquéritos de satisfação e os testes estatísticos.
Para avaliar a solução final de acordo com as hipóteses e grandezas definidas, podemos optar
pelo teste One-Tailed Test (paramétrico) ou Two-Tailed Test (não-paramétrico).
Segundo (Stone 2019), antes de optar por um dos testes devemos analisar o que pretende-
mos testar. A sua sugestão recai sobre duas questões:
1. O resultado é maior (ou menor que) um determinado valor?
2. O resultado está dentro ou fora de um determinado intervalo de valores?
Se estivermos presentes sobre o primeiro cenário, a opção recai para o teste paramétrico,
enquanto que o segundo cenário de enquadra no teste não-paramétrico.
Paramétrico (Ruxton e Neuhäuser 2010) e (University 2018) referem que um teste para-
métrico resulta de uma hipótese alternativa que especifica uma direção. Ou seja, quando a
hipótese alternativa afirma que o parâmetro é de fato maior ou menor que o valor especifi-
cado na hipótese nula.
O teste paramétrico pode ser à esquerda, H1 : µ < µ0 onde é usado quando a hipótese
alternativa afirma que o valor verdadeiro do parâmetro especificado na hipótese nula é menor
do que a hipótese nula reivindicada. Um teste paramétrico à direita H1 : µ > µ0 é usado
quando a hipótese alternativa afirma que o valor verdadeiro do parâmetro especificado na
hipótese nula é maior do que as hipóteses de hipótese nula.
Esta secção irá apresentar os resultados obtidos deste estudo de caso de acordo com as
hipóteses definidas anteriormente. De modo a avaliar de forma coerente e precisa, algumas
premissas foram definidas para que fosse seguida uma lógica de avaliação comum entre os
indicadores.
6.6. Avaliação de resultados 89
• Período temporal: O período temporal foi definido com base na data de início da
implementação deste estudo de caso na equipa de trabalho até 22 semanas depois, ou
seja, o período temporal utilizado foi entre 11/02/2019 a 15/07/2019, que reflete um
total de 11 sprints. Como alguns dos indicadores de avaliação requerem um período
de comparação para ser possível comprar resultados, foi utilizado o mesmo período
de tempo, 11 sprints, antes da implementação deste estudo de caso, ou seja, de
10/09/2018 a 11/02/2019. De modo a facilitar a referencia a estes dois períodos
temporais ao longo desta secção, o período referente à aplicação deste caso de estudo
será referenciado como Período Estudo de caso e o período referente a antes da
aplicação como Período Modelo Tradicional.
• Inquiridos: Relativamente ao inquérito realizado A, os inquiridos responderam às ques-
tões com base no mesmo período temporal. Todos eles fazem parte da equipa que
integrou este estudo de caso, tendo a oportunidade de experiênciar todos os conceitos
aplicados ao longo deste período.
Por fim, a figura 6.1, apresenta a matriz de respostas por parte dos inquiridos. No total, 7
pessoas responderam ao questionário, sendo que todas elas responderam também a todas as
questões. Perante este cenário, podemos considerar que todas as amostras são quantitativas,
independentes, mas não podemos assumir uma distribuição normal, sendo a amostra inferior
a 30 resultados.
Para avaliar este indicador, foram utilizadas as seguintes questões do questionário realizado
aos membros da equipa:
• Questão 1 - Consideras que a solução desenhada se enquadra no âmbito da equipa?
• Questão 2 - Consideras que a solução desenhada ajuda de forma contínua a resolver
os problemas identificados?
• Questão 4 - Consideras o uso do TDD uma mais valia durante o processo de desen-
volvimento de forma a melhorar o software produzido?
• Questão 5 - Consideras o uso do BDD benéfico durante o processo de desenvolvimento
de forma a validar o comportamento esperado do software?
90 Capítulo 6. Avaliação
Para avaliar a satisfação da equipa com base na métrica definida, onde refere que é expectável
que a equipa se sinta confortável, com um grau de satisfação superior a 90%, irão ser
avaliadas as questões referidas de forma singular de acordo com a métrica definida.
De acordo com 6.1, com intervalo de confiança de 95%, podemos concluir que como o p-
value é inferior a 5% (nível de significância), a amostra não segue uma distribuição normal,
logo um t-test não pode ser usado, sendo que irá ser aplicado um Wilcoxon teste, que é um
teste não paramétrico.
De acordo com a definição de um grau de satisfação superior a 90% para esta questão,
podemos considerar as seguintes hipóteses:
H0: Média de respostas é menor ou igual a 4.5
H1: Média de respostas é maior que 4.5
1 > w i l c o x . t e s t ( q1 , mu = 4 . 5 , alternative = " greater ")
2
3 Wilcoxon signe d rank t e s t with c o n t i n u i t y correction
4
5 data : q1
6 V = 2 4 , p−v a l u e = 0 . 0 3 6 3
7 alternative hypothesis : true location is g r e a t e r than 4.5
Após analisar o cálculo 6.2,como o p-value é inferior a 0.05, a hipótese nula é rejeitada, logo
com 95% de confiança podemos concluir que a equipa considera que a solução se enquadra
no seu âmbito.
Questão 5 Irá ser utilizado um teste Shapiro-Wilk para verificar o tipo de distribuição,
pois não podemos assumir que estamos perante uma distribuição normal.
H0: A população segue uma distribuição normal
H1: A população não segue uma distribuição normal
O código presente em 6.3 apresenta o teste de Shapiro-Wilks realizado com a amostra de
respostas relativas à questão 5.
1 > q1<−c ( 4 , 4 , 5 , 5 , 5 , 4 , 5 )
2 > s h a p i r o . t e s t ( q1 )
3
4 S h a p i r o −W i l k n o r m a l i t y test
5
6 data : q1
7 W = 0 . 6 6 4 4 4 , p−v a l u e = 0 . 0 0 1 4 9 7
De acordo com 6.1, com intervalo de confiança de 95%, podemos concluir que como o p-
value é inferior a 5% (nível de significância), a amostra não segue uma distribuição normal,
logo um t-test não pode ser usado, sendo que irá ser aplicado um Wilcoxon teste, que é um
teste não paramétrico.
De acordo com a definição de um grau de satisfação superior a 90% para esta questão,
podemos considerar as seguintes hipóteses:
H0: Média de respostas é menor ou igual a 4.5
H1: Média de respostas é maior que 4.5
1 > w i l c o x . t e s t ( q1 , mu = 4 . 5 , alternative = " greater ")
2
3 Wilcoxon sign ed rank t e s t with c o n t i n u i t y correction
4
5 data : q1
6 V = 1 6 , p−v a l u e = 0 . 3 8 8 4
7 alternative hypothesis : true location is g r e a t e r than 4.5
Após analisar o cálculo 6.4, como o p-value é superior a 0.05, a hipótese nula não pode ser
rejeitada, logo com 95% de confiança podemos concluir que a equipa não considera o uso do
BDD benéfico durante o processo de desenvolvimento de forma a validar o comportamento
esperado do software.
Questão 3 Para avaliar esta questão, irá ser utilizado um teste Shapiro-Wilk para verificar
o tipo de distribuição.
H0: A população segue uma distribuição normal
H1: A população não segue uma distribuição normal
O código presente em 6.5 apresenta o teste de Shapiro-Wilks realizado com a amostra de
respostas relativas à questão 3.
1 > q1<−c ( 4 , 4 , 5 , 4 , 5 , 5 , 5 )
2 > s h a p i r o . t e s t ( q1 )
3
4 S h a p i r o −W i l k n o r m a l i t y test
5
6 data : q1
7 W = 0 . 6 6 4 4 4 , p−v a l u e = 0 . 0 0 1 4 9 7
De acordo com 6.5, com intervalo de confiança de 95%, podemos concluir que como o
p-value é inferior a 5%, a amostra não segue uma distribuição normal, logo um Wilcoxon
teste irá ser usado.
De acordo com a definição de um grau de satisfação superior a 80% para esta questão,
podemos considerar as seguintes hipóteses:
H0: Média de respostas é menor ou igual a 4
H1: Média de respostas é maior que 4
1 > w i l c o x . t e s t ( q1 , mu = 4 , a l t e r n a t i v e = " g r e a t e r " )
2
3 Wilcoxon signe d rank t e s t with c o n t i n u i t y correction
4
5 data : q1
6 V = 1 0 , p−v a l u e = 0 . 0 3 5 9 3
7 alternative hypothesis : true location is g r e a t e r than 4
Após analisar o resultado de 6.6, como o p-value é inferior a 0.05, a hipótese nula pode
ser rejeitada, logo com 95% de confiança podemos concluir que a equipa adotou de forma
contínua a solução desenhada durante o processo de desenvolvimento.
6.6. Avaliação de resultados 93
Monitorização do uso da nova solução Para medir este indicador, foi utilizado o período
temporal Estudo de Caso.
Foi realizada uma pesquisa a todas as tarefas realizadas pela equipa dentro desse período
temporal e a amostra total de tarefas foi de 83. Após aplicar filtros sobre a amostra,
nomeadamente o valor da etiqueta que reflete o uso da nova solução, foram retirados os
números correspondeste ao uso da nova solução e também ao uso do processo tradicional.
A tabela 6.1 apresenta com mais detalhe os números da amostra.
De acordo com a tabela 6.1, podemos concluir que cerca de 84% das tarefas foram marcadas
com a etiqueta que refere a solução que este estudo de caso propõe, sendo este valor superior
as 80% defendidos pela hipótese, podemos considerar que a solução foi utilizada e aplicada
dentro do esperado.
base nos projetos que a equipa tem na sua posse. A tabela 6.3 apresenta o número de
defeitos no código relativos aos dois períodos temporais.
Analisando os dados presentes na tabela 6.3, o número de defeitos no código em produção
diminuiu cerca de 94% com o uso da nova solução, conclui-se que a nova metodologia
aplicada neste estudo de caso foi uma autêntica mais valia a diminuir o número de defeitos
no código em produção.
Sendo um dos pontos chave desta solução a utilização de testes, o tempo de execução
da bateria de testes tem um grande impacto no desenvolvimento, feedback contínuo e na
velocidade de entrega de valor. Para garantir que o tempo total de execução não ultrapassa-
se os 100 segundos, foi realizada uma monitorização da execução da bateria de testes
nas maquinas dos membros da equipa e nas maquinas onde são executadas as pipelines, e
para além disso, foi utilizada uma questão, de modo a perceber se os membros da equipa
consideram a bateria de testes rápida, escalável, eficiente e eficaz a garantir a qualidade da
solução.
Pipeline Para avaliar estes valores, irá ser utilizado um teste Shapiro-Wilk para verificar o
tipo de distribuição para o tempo de execução da pipeline.
96 Capítulo 6. Avaliação
De acordo com 6.7, com intervalo de confiança de 95%, podemos concluir que como o
p-value é superior a 5% a amostra segue uma distribuição normal, logo um t-test pode ser
usado.
De acordo com a definição tempo máximo de execução de 100 segundos, podemos considerar
as seguintes hipóteses:
H0: Média de tempo de execução é igual ou superior a 100 segundos
H1: Média de tempo de execução é inferior a 100 segundos
1 > t . t e s t ( q1 , mu= 1 0 0 , a l t e r n a t i v e = " l e s s " , c o n f . l e v e l = 0 . 9 5 )
2
3 One S a mp l e t− t e s t
4
5 data : q1
6 t = −9.9693 , d f = 1 9 , p−v a l u e = 2 . 7 6 5 e −09
7 a l t e r n a t i v e h y p o t h e s i s : t r u e mean i s l e s s t h a n 100
8 95 p e r c e n t c o n f i d e n c e i n t e r v a l :
9 −I n f 89.8747
10 sample e s t i m a t e s :
11 mean o f x
12 87.75
Analisando os resultados presentes em 6.8, como o p-value é inferior inferior a 5%, a hipótese
nula é rejeitada, logo podemos concluir com 95% de confiança que o tempo de execução
da pipeline é inferior a 100 segundos.
Máquina local O mesmo método irá ser utilizado, um teste Shapiro-Wilk para verificar o
tipo de distribuição para o tempo de execução nas maquinas locais.
H0: A população segue uma distribuição normal
H1: A população não segue uma distribuição normal
1 > q1<−c ( 4 3 , 4 2 , 4 4 , 4 1 , 4 2 , 4 3 , 4 3 , 4 2 , 4 2 , 3 9 , 3 8 , 3 8 , 3 0 , 4 1 , 3 4 , 3 3 , 3 3 , 3 4 , 3 4 , 3 5 )
2 > s h a p i r o . t e s t ( q1 )
3
4 S h a p i r o −W i l k n o r m a l i t y test
5
6 data : q1
7 W = 0 . 8 8 7 0 8 , p−v a l u e = 0 . 0 2 3 7 7
De acordo com 6.9, com intervalo de confiança de 95%, podemos concluir que como o
p-value é inferior a 5% a amostra não segue uma distribuição normal, logo um Wilcoxon
teste terá de ser usado.
De acordo com a definição tempo máximo de execução de 100 segundos, podemos considerar
as seguintes hipóteses:
H0: Média de tempo de execução é igual ou superior a 100 segundos
H1: Média de tempo de execução é inferior a 100 segundos
1 > w i l c o x . t e s t ( q1 , mu = 1 0 0 , a l t e r n a t i v e = " l e s s " )
2
3 Wilcoxon sign ed rank t e s t with c o n t i n u i t y correction
4
5 data : q1
6 V = 0 , p−v a l u e = 4 . 6 4 5 e −05
7 alternative hypothesis : true location is l e s s t h a n 100
De acordo com 6.5, com intervalo de confiança de 95%, podemos concluir que como o p-
value é inferior a 5% (nível de significância), a amostra não segue uma distribuição normal,
sendo que irá ser aplicado um Wilcoxon teste.
De acordo com a definição de um grau de satisfação superior a 80% para esta questão,
podemos considerar as seguintes hipóteses:
98 Capítulo 6. Avaliação
Após analisar o cálculo 6.12,como o p-value é inferior a 0.05, a hipótese nula é rejeitada,
logo com 95% de confiança podemos concluir que a equipa considera que a bateria de testes
é rápida, escalável, eficiente e eficaz a garantir a qualidade da solução.
99
Capítulo 7
Conclusão
7.1 Introdução
O presente capítulo tem como propósito apresentar as conclusões finais de todo o trabalho
desenvolvido ao longo deste estudo de caso, os resultados obtidos, e por fim, o trabalho
futuro.
7.2 Resultados
O principal objetivo deste estudo de caso foi desenhar, desenvolver, implementar e monito-
rizar um novo processo de desenvolvimento e aplica-lo sobre a equipa de desenvolvimento,
de forma a responder à hipótese central desta dissertação, Será que a aplicação do desen-
volvimento orientado aos testes, em contexto empresarial, permitirá melhorar a qualidade
dos produtos e do processo de desenvolvimento de software?.
Após uma análise ao capítulo 6, podemos concluir que:
• Com um grau de satisfação superior a 90%, a equipa considerou que a solução dese-
nhada se enquadra no âmbito da equipa, foi implementada com sucesso e adotada de
forma contínua ao longo do tempo, com 84% das tarefas a serem marcadas com o
selo TDD&BDD. Com isto, o processo de desenvolvimento ficou mais robusto, pois
a definição dos critérios e testes de aceitação foram definidos em conjunto por toda a
equipa, garantindo que todos os membros estão na mesma linha de pensamento.
• A solução contribuiu para uma aumento da velocidade da equipa, cerca de 8% face
ao processo tradicional. Com este aumento, a equipa foi capaz de entregar mais valor
para os seus stakeholders.
• O número de defeitos no código diminuiu drasticamente com o uso da nova solução,
onde podemos evidenciar uma diminuição de cerca de 94%. Concluímos assim que
a nova solução aplicada foi um enorme sucesso no que diz respeito à garantia da
qualidade do produto entregue, apresentado um grau de defeitos muito baixo. Isto
permite a que a equipa fique focada em novos desenvolvimentos e não perca tempo a
analisar e realizar correções sobre aplicações em produção, que implicam repetir todo
o fluxo de desenvolvimento, teste, aprovação e release.
100 Capítulo 7. Conclusão
usar esta solução, ou parte dela, para melhorar os seus processos, produtos, velocidade de
entrega, qualidade, etc.
Bibliografia
Carvalho, Rogerio Atem de, Rodrigo Soares Manhães et al. (2010). «Filling the Gap between
Business Process Modeling and Behavior Driven Development». Em: arXiv preprint ar-
Xiv:1005.4975.
Causevic, Adnan, Daniel Sundmark e Sasikumar Punnekkat (2011). «Factors limiting indus-
trial adoption of test driven development: A systematic review». Em: 2011 Fourth IEEE
International Conference on Software Testing, Verification and Validation. IEEE, pp. 337–
346.
Cheon, Yoonsik e Gary T Leavens (2002). «A simple and practical approach to unit testing:
The JML and JUnit way». Em: European Conference on Object-Oriented Programming.
Springer, pp. 231–255.
De Carvalho, Rogério Atem e Renato De Campos (2006). «A development process pro-
posal for the ERP5 system». Em: Systems, Man and Cybernetics, 2006. SMC’06. IEEE
International Conference on. Vol. 6. IEEE, pp. 4703–4708.
De Lucia, Andrea e Abdallah Qusef (2010). «Requirements engineering in agile software
development». Em: Journal of emerging technologies in web intelligence 2.3, pp. 212–
220.
DeMillo, Richard A, Richard J Lipton e Frederick G Sayward (1978). «Hints on test data
selection: Help for the practicing programmer». Em: Computer 11.4, pp. 34–41.
Docker overview (2019). url: https://docs.docker.com/engine/docker-overview/.
Elbaum, Sebastian, David Gable e Gregg Rothermel (2001). «The impact of software evo-
lution on code coverage information». Em: Proceedings of the IEEE International Confe-
rence on Software Maintenance (ICSM’01). IEEE Computer Society, p. 170.
Fallah, Farzan, Srinivas Devadas e Kurt Keutzer (2001). «OCCOM-efficient computation
of observability-based code coverage metrics for functional verification». Em: IEEE Tran-
sactions on computer-aided design of integrated circuits and systems 20.8, pp. 1003–
1015.
Ficco, Massimo, Roberto Pietrantuono e Stefano Russo (2011). «Bug localization in test-
driven development». Em: Advances in Software Engineering 2011, p. 2.
FluentAssertions (2019). FluentAssertions. url: https://fluentassertions.com (acedido
em 30/06/2019).
Focus, Micro (2016). Continuous Delivery: Automating the Deployment Pipeline. url: https:
//www.microfocus.com/media/white-paper/continuous_delivery_automating_
the_deployment_pipeline_wp.pdf.
Foundation, .NET. About xUnit.net. url: https://xunit.github.io/.
Fowler, Martin (2014). UnitTest. url: https://martinfowler.com/bliki/UnitTest.
html.
Fowler, Martin e Matthew Foemmel (2006). «Continuous integration». Em: Thought-Works)
http://www. thoughtworks. com/Continuous Integration. pdf 122, p. 14.
George, Boby e Laurie Williams (2003). «An initial investigation of test driven development
in industry». Em: Proceedings of the 2003 ACM symposium on Applied computing. ACM,
pp. 1135–1139.
Gliem, Joseph A e Rosemary R Gliem (2003). «Calculating, interpreting, and reporting
Cronbach’s alpha reliability coefficient for Likert-type scales». Em: Midwest Research-to-
Practice Conference in Adult, Continuing, e Community . . .
Gojare, Satish, Rahul Joshi e Dhanashree Gaigaware (2015). «Analysis and Design of Sele-
nium WebDriver Automation Testing Framework». Em: Procedia Computer Science 50,
pp. 341–346.
Guidelines for Test-Driven Development (2006). url: https://msdn.microsoft.com/en-
%20us/library/aa730844(v=vs.80).aspx.
BIBLIOGRAFIA 105
Gundecha, Unmesh (2012). Selenium Testing Tools Cookbook. Packt Publishing Ltd.
Hembrink, J e P Stenberg (2013). «Continuous integration with jenkins». Em: Coaching of
Programming Teams (EDA 270), Faculty of Engineering, Lund University, LTH, p. 23.
Hendrickson, Elisabeth (2008). «Driving development with tests: ATDD and TDD». Em:
STARWest 2008.
Hetzel, William C e Bill Hetzel (1988). The complete guide to software testing. QED Infor-
mation Sciences Wellesley, MA.
Holmes, Antawan e Marc Kellogg (2006). «Automating functional tests using selenium».
Em: Agile Conference, 2006. IEEE, 6–pp.
Hsia, Pei et al. (1994). «Behavior-based acceptance testing of software systems: a formal
scenario approach». Em: Computer Software and Applications Conference, 1994. COMP-
SAC 94. Proceedings., Eighteenth Annual International. IEEE, pp. 293–298.
Introducing BDD (2006). url: https://dannorth.net/introducing-bdd/.
Janzen, David S (2005). «Software architecture improvement through test-driven develop-
ment». Em: Companion to the 20th annual ACM SIGPLAN conference on Object-oriented
programming, systems, languages, and applications, p. 240.
Janzen, David S e Hossein Saiedian (2006). «On the influence of test-driven development on
software design». Em: Software Engineering Education and Training, 2006. Proceedings.
19th Conference on. IEEE, pp. 141–148.
– (2008). «Does test-driven development really improve software design quality?» Em: Ieee
Software 25.2, p. 77.
Jeffries, Ron, Ann Anderson e Chet Hendrickson (2001). Extreme programming installed.
Addison-Wesley Professional.
Jeffries, Ron e Grigori Melnik (2007). «TDD: The Art of Fearless Programming». Em: IEEE
SOFTWARE 2, p. 5.
Jenkins (2018). url: http://jenkins.io/doc/.
Jia, Yue e Mark Harman (2011). «An analysis and survey of the development of mutation
testing». Em: IEEE transactions on software engineering 37.5, pp. 649–678.
Kaur, Rupinder e Jyotsna Sengupta (2013). «Software process models and analysis on failure
of software development projects». Em: arXiv preprint arXiv:1306.1068.
Kim, Taesoo, Ramesh Chandra e Nickolai Zeldovich (2013). «Optimizing unit test execution
in large software programs using dependency analysis». Em: Proceedings of the 4th Asia-
Pacific Workshop on Systems. ACM, p. 19.
Kock, Ned (2015). «One-tailed or two-tailed P values in PLS-SEM?» Em: International
Journal of e-Collaboration (IJeC) 11.2, pp. 1–7.
Koen, Peter et al. (2001). «Providing clarity and a common language to the “fuzzy front
end”». Em: Research-Technology Management 44.2, pp. 46–55.
Kumar, Shaweta e Sanjeev Bansal (2013). «Comparative study of test driven development
with traditional techniques». Em: Int J Soft Comput Eng (IJSCE) 3.1, pp. 2231–2307.
Layman, Lucas et al. (2006). «Essential communication practices for Extreme Programming
in a global software development team». Em: Information and software technology 48.9,
pp. 781–794.
Leung, Hareton KN e Lee White (1990). «A study of integration testing and software regres-
sion at the integration level». Em: Software Maintenance, 1990, Proceedings., Conference
on. IEEE, pp. 290–301.
Lindstrom, Lowell e Ron Jeffries (2004). «Extreme programming and agile software deve-
lopment methodologies». Em: Information systems management 21.3, pp. 41–52.
Luo, Lu (2001). «Software testing techniques». Em: Institute for software research interna-
tional Carnegie mellon university Pittsburgh, PA 15232.1-19, p. 19.
106 BIBLIOGRAFIA
Mackinnon, Tim, Steve Freeman e Philip Craig (2000). «Endo-testing: unit testing with
mock objects». Em: Extreme programming examined, pp. 287–301.
Marick, Brian et al. (1999). «How to misuse code coverage». Em: Proceedings of the 16th
Interational Conference on Testing Computer Software, pp. 16–18.
Marri, Madhuri R et al. (2009). «An empirical study of testing file-system-dependent software
with mock objects». Em: Automation of Software Test, 2009. AST’09. ICSE Workshop
on. IEEE, pp. 149–153.
Martinez-Hernandez, Veronica (2003). «Understanding value creation: the value matrix and
the value cube». Tese de doutoramento. University of Strathclyde.
Microsoft (2019a). Create web APIs with ASP.NET Core. url: https://docs.microsoft.
com/en-us/aspnet/core/web-api/?view=aspnetcore-2.2.
– (2019b). Integration tests in ASP.NET Core. url: https://docs.microsoft.com/en-
us/aspnet/core/test/integration-tests?view=aspnetcore-2.2.
– (2019c). Microsoft.AspNetCore.Mvc.Testing. url: https://www.nuget.org/packages/
Microsoft.AspNetCore.Mvc.Testing (acedido em 30/06/2019).
Minimal e Tom Akehurst (2019). WireMock. url: http://wiremock.org/.
Mongo2Go (2019a). Mongo2Go. url: https://github.com/Mongo2Go/Mongo2Go.
– (2019b). Mongo2Go. url: https : / / github . com / Mongo2Go / Mongo2Go (acedido em
30/06/2019).
Moq (2019). Moq. url: https://github.com/moq/moq4 (acedido em 30/06/2019).
Nagappan, Nachiappan et al. (2008). «Realizing quality improvement through test driven
development: results and experiences of four industrial teams». Em: Empirical Software
Engineering 13.3, pp. 289–302.
Offutt, A Jefferson (1994). «A practical system for mutation testing: help for the common
programmer». Em: Test Conference, 1994. Proceedings., International. IEEE, pp. 824–
830.
Osterwalder, Alexander e Yves Pigneur (2010). Business model generation: a handbook for
visionaries, game changers, and challengers. John Wiley & Sons.
– (2011). «Aligning profit and purpose through business model innovation». Em: Responsible
management practices for the 21st century, pp. 61–75.
Pan, Jiantao (1999). «Software testing». Em: Dependable Embedded Systems 5, p. 2006.
Pereira, Lauriane et al. (2018). «Behavior-driven development benefits and challenges: re-
ports from an industrial study». Em: Proceedings of the 19th International Conference on
Agile Software Development: Companion. ACM, p. 42.
Prouse, Rob et al. (2018). Unit testing C with NUnit and .NET Core - .NET Core. url:
https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-
nunit.
Reese, John, Andy De George e Maira Wenzel (2018). Best practices for writing unit tests -
.NET Core. url: https://docs.microsoft.com/en-us/dotnet/core/testing/unit-
testing-best-practices.
Rendell, Andrew (2008). «Effective and pragmatic test driven development». Em: Agile 2008
Conference. IEEE, pp. 298–303.
Rpetrusha (2017). Live Unit Testing - Visual Studio. url: https://docs.microsoft.com/
en-us/visualstudio/test/live-unit-testing?view=vs-2017.
– (2019). Live Unit Testing - Visual Studio. url: https : / / docs . microsoft . com / en -
us/visualstudio/test/live-unit-testing?view=vs-2017.
Ruxton, Graeme D e Markus Neuhäuser (2010). «When should we use one-tailed hypothesis
testing?» Em: Methods in Ecology and Evolution 1.2, pp. 114–117.
BIBLIOGRAFIA 107
Santelices, Raul et al. (2009). «Lightweight fault-localization using multiple coverage types».
Em: Proceedings of the 31st International Conference on Software Engineering. IEEE
Computer Society, pp. 56–66.
Sauvé, Jacques Philippe, Osório Lopes Abath Neto e Walfredo Cirne (2006). «EasyAccept:
a tool to easily create, run and drive development with automated acceptance tests». Em:
Proceedings of the 2006 international workshop on Automation of software test. ACM,
pp. 111–117.
Schwaber, Ken e Mike Beedle (2002). Agile software development with Scrum. Vol. 1.
Prentice Hall Upper Saddle River.
Shull, Forrest et al. (2010). «What do we know about test-driven development?» Em: IEEE
software 27.6, pp. 16–19.
Solis, Carlos e Xiaofeng Wang (2011). «A study of the characteristics of behaviour driven
development». Em: Software Engineering and Advanced Applications (SEAA), 2011 37th
EUROMICRO Conference on. IEEE, pp. 383–387.
Sommerville, Ian (1996). «Software process models». Em: ACM computing surveys (CSUR)
28.1, pp. 269–271.
Specflow (2019). SpecFlow Documentation. url: https://specflow.org/documentation/
faq/.
Stolberg, Sean (2009). «Enabling agile testing through continuous integration». Em: Agile
Conference, 2009. AGILE’09. IEEE, pp. 369–374.
Stone, David (2019). Stats Tutorial - Instrumental Analysis and Calibration. url: https:
//www.chem.utoronto.ca/coursenotes/analsci/stats/12tailed.html.
Thomas, Christopher M (2014). «An Overview of the Current State of the Test-First vs.
Test-Last Debate». Em: Scholarly Horizons: University of Minnesota, Morris Undergra-
duate Journal 1.2, p. 2.
Thomas, Dave e Andy Hunt (2002). «Mock objects». Em: IEEE Software 19.3, pp. 22–24.
Tsai, Wei-Tek et al. (2001). «End-to-end integration testing design». Em: Computer Soft-
ware and Applications Conference, 2001. COMPSAC 2001. 25th Annual International.
IEEE, pp. 166–171.
Umesh, Nitin e Amar Saraswat (2015). «Automation Testing: An Introduction to Selenium».
Em: International Journal of Computer Applications 119.3.
University, Newcastle (2018). Hypothesis Testing. url: https://internal.ncl.ac.uk/
ask/numeracy-maths-statistics/statistics/hypothesis-testing/one-tailed-
and-two-tailed-tests.html.
Value Analysis (2005). url: http : / / www . innovation - portal . info / wp - content /
uploads/Value-Analysis.pdf.
Wappler, Stefan e Joachim Wegener (2006). «Evolutionary unit testing of object-oriented
software using strongly-typed genetic programming». Em: Proceedings of the 8th annual
conference on Genetic and evolutionary computation. ACM, pp. 1925–1932.
WireMock-Net (2019). WireMock-Net/WireMock.Net. url: https://github.com/WireMock-
Net/WireMock.Net.
Woodall, Tony (2003). «Conceptualising ‘value for the customer’: an attributional, structural
and dispositional analysis». Em: Academy of marketing science review 12.1, pp. 1–42.
Wright, Joe (2009). The Coding Dojo. url: https : / / code . joejag . com / 2009 / the -
coding-dojo.html.
Wynne, Matt, Aslak Hellesoy e Steve Tooke (2017). The cucumber book: behaviour-driven
development for testers and developers. Pragmatic Bookshelf.
108 BIBLIOGRAFIA
Apêndice A
Inquérito de satisfação