1. Teste de Software de A a Z
Palestrante: Camilo Porto
https://www.linkedin.com/in/camiloporto
2. Sumário
Motivação tradicional para Teste de Software
Testes sob outra óptica
Escrever teste antes ou depois de Codificar? Avaliando Impactos
Design X Testabilidade
Importância da automação e ferramentas
4. Primeiramente, o que é um teste?
Teste = <cenário, lógica a validar, verificações>
Exemplo
Cenário: parcela1 = 6; parcela2 = 9
Lógica a validar: soma(parcela1, parcela2) : resultado
verificações: resultado == 15
Escrever um teste, portanto, consiste em:
Definir o cenário;
Executar a lógica no contexto do cenário criado
5. Pesquisa Rápida...
Quem acha que testar código é importante?
Quem acha difícil testar código?
Quem acha chato?
Quem escreve teste para o código que escreve?
8. Testes sob outra óptica
Visão mais abrangente da prática de teste de software
9. Já pensou em escrever teste para...
Definir/Expressar onde se quer chegar (objetivo/escopo) com o código que
você irá começar a escrever?
“Vou codificar um cadastro de usuário...”
Já pensou em expressar isso em Java? Com um teste?
10. Já pensou em escrever teste para...
Definir como a nova funcionalidade será usada (Interface de Uso)?
“Minha classe de negócio vai receber um objeto Usuário com email, nome e rg e retornará um
Relatório da Operação informando se o cadastro foi realizado com sucesso ou com uma lista
de erros ocorridos”
Já pensou em expressar isso em linha de código?
11. Já pensou em escrever teste para...
Dar foco ao trabalho de codificação...
“O cadastro de usuário deve:
● Validar entradas
● Inserir dados no banco de dados
● enviar email para o usuário ativar cadastro”
Por onde começar?
Já tentou expressar com um teste somente o “próximo passo”?
12. Já pensou em escrever teste para...
Melhorar o design do seu código (acoplamento, coesão, ..)
13. Já pensou em escrever teste para...
Documentar o Software?
O que vocês acham que a classe
PaísRepository faz, olhando
somente para a sua suíte de teste?
Consulta um País pelo número de
habitantes?
Consulta um país pelo seu nome?
Como você acha que pode usar essa
classe, olhando somente para um
de seus testes?
public void deveConsultarPorNome() {
String nome = “brasil”
List<Pais> resultado = paisRepository.get(nome)
…. …..
}
14. Já pensou em escrever teste para...
Debugar o Software?
16. Já pensou em escrever teste para...
Melhorar manutenibilidade?
Você se sente mais seguro em manter um software com uma bateria robusta
de testes?
Se você fizer bobagem, qual a chance de o teste te alertar?
E se não existir teste?
Já tentou fazer uma grande refatoração em software sem teste? (Atualizar uma
biblioteca para uma nova versão? usar um novo componente?)
17. Já pensou em escrever teste para...
Aumentar velocidade do desenvolvimento?
Parece contraditório??
Quanto tempo se leva para:
Entregar nova função em produção
Em um software com 60.000 LoC
● Sem testes..
● Com testes?
“... isso estava funcionando.. mas agora, não sei por que, parou de
21. Depois de a função codificada...
● Lógica já está definida;
● Interface de uso já definida
● Design (classes, métodos, acoplamentos, responsabilidades) já elaborado.
● Nesse contexto, qual é o papel do teste?
○ Confirmar comportamento esperado
○ Cobrir cenários excepcionais.
22. Depois de a função codificada...
Pontos de atenção
● O escopo da função foi definido em função do cliente (classe que irá invocá-
la) ou o foco tende a ser o algoritmo?
● Pensou-se em deixar o código testável? Ou, somente no momento de
escrever o teste, identificou-se uma dificuldade/inviabilidade de testar o
código?
● O código já ta pronto… há realmente necessidade de escrever teste?
○ “Eu já vi que funciona”
23. Depois de a função codificada...
Resumo
● Atenção e foco tende a recair no algoritmo, puro e simplesmente
● Análise de cenários possíveis (“what if”), que podem não se concretizar, e a
definição sobre o que fazer tendem a ocorrer de forma caótica e simultânea
○ “E se for null? E se x < 13?”
○ NullPointerException? IllegalArgumentException? return -1?
○ Vai fazer sentido para o cliente da função?
● Tendência de ficar em segundo plano
25. Cadastro de Usuário...
Receber informações de nome, email e senha do usuário;
Realizar validações e persistir informações
Sinalizar se sucesso ou erro
Enviar email com link para ativação da conta.
26. Expressando como um teste….
class CadastroUsuarioTest {
} Vou fazer o
Cadastro de
Usuários
27. Expressando como um teste….
class CadastroUsuarioTest {
deveInserirNovoUsuarioNoBanco() {
}
}
Vou começar fazendo a
parte que insere os
dados no banco…
Começando no cenário
de inserir um novo
usuário...
28. Expressando como um teste….
class CadastroUsuarioTest {
CadastroUsuario cadastro = ...;
deveInserirNovoUsuarioNoBanco() {
Usuario u = new Usuario()
//u.set... com valores bem comportados..
Usuario salvo = cadastro.salvaUsuario(u)
assertNotNull(salvo.getId())
}
}
Quem for usar essa função, vai
me passar as informações dentro
de um objeto Usuario…
.. e vai chamar o método
salvaUsuario.
… vou retornar o mesmo objeto,
com a informação do ID gerado
pelo banco como sinal de que
tudo deu certo.
29. Cenário após conclusão do raciocínio
class CadastroUsuarioTest {
CadastroUsuario cadastro = ...;
deveInserirNovoUsuarioNoBanco() {
Usuario u = new Usuario()
//u.set... com valores bem comportados..
Usuario salvo = cadastro.salvaUsuario(u)
assertNotNull(salvo.getId())
}
}
código do teste código de produção
31. Definimos Interface de Uso?
nome: salvaUsuario
recebe: um único argumento cujo tipo é Usuario
retorna
se sucesso: mesmo objeto de entrada, com ID atribuído;
se falha: ???? (ainda não pensamos sobre isso… o foco ainda não chegou neste ponto).
exceções: ???
32. Definimos o foco/escopo/objetivo do trabalho?
O que você vai fazer agora?
A.Um enviador de email que manda o link de ativação quando o usuário é
cadastrado?
B.Validar se as informações do usuário são válidas?
C.Tratar o caso onde o argumento ‘Usuario’ for nulo?
D.Algum código que persita as informações do Usuário e gere uma
identificação para ele.
33. Documentamos nosso código?
No momento, o que deve fazer a classe CadastroUsuario?
A.deve remover usuários?
B.deve enviar emails de ativação?
C.deve inserir novo usuário?
D.deve lançar alguma exceção caso algo de errado ocorra?
34. Afetamos o design do código de produção?
Quanto à testabilidade…
Fizemos um código de produção testável?
Nem mesmo escrevemos o código, mas seja ele qual for, não só vai nascer
testável, como TESTADO!
36. Implementado a função
class CadastroUsuarioTest {
CadastroUsuario cadastro = ...;
deveInserirNovoUsuarioNoBanco() {
Usuario u = new Usuario()
//u.set... com valores bem comportados..
Usuario salvo = cadastro.salvaUsuario(u)
assertNotNull(salvo.getId())
}
}
código do teste código de produção
class CadastroUsuario {
usuario salvaUsuario(Usuario u) {
Long idGerado = bancoDeDados.insere(u);
u.setId(idGerado);
return u;
}
}
37. Cenário após a codificação
class CadastroUsuarioTest {
CadastroUsuario cadastro = ...;
deveInserirNovoUsuarioNoBanco() {
Usuario u = new Usuario()
//u.set... com valores bem comportados..
Usuario salvo = cadastro.salvaUsuario(u)
assertNotNull(salvo.getId())
}
}
código do teste código de produção
class CadastroUsuario {
usuario salvaUsuario(Usuario u) {
Long idGerado = bancoDeDados.insere(u);
u.setId(idGerado);
return u;
}
}
✓ Testável
✓ Testado (cobertura de testes)
✓ Justo (nada de “what-if”)
✓ Refatoração segura
40. Design e Testabilidade
Design do código impacta a testabilidade
Baixa testabilidade pode sinalizar pontos de atenção no design (code smell)
Analisemos alguns cenários...
41. new… Vs Injeção de Dependência
class CadastroUsuario {
BancoDeDados bancoDados;
usuario salvaUsuario(Usuario u) {
bancoDados = new BancoDeDadosOracleDaInfraEstrutura()
Long idGerado = bancoDeDados.insere(u);
u.setId(idGerado);
return u;
}
}
42. new… Vs Injeção de Dependência
class CadastroUsuarioTest {
CadastroUsuario cadastro = ...;
deveInserirNovoUsuarioNoBanco() {
Usuario u = new Usuario()
//u.set... com valores bem comportados..
Usuario salvo = cadastro.salvaUsuario(u)
assertNotNull(salvo.getId())
}
}
● Em qual banco será inserido?
● Como faço para, ao testar, usar
outro banco?
● Consigo configurar facilmente
CadastroUsuario para um banco
de testes?
43. new… Vs Injeção de Dependência
Viabilizando o teste...
class CadastroUsuario {
BancoDeDados bancoDados //… recebe por Injeção de Dependência;
usuario salvaUsuario(Usuario u) {
bancoDados = new BancoDeDadosOracleDaInfraEstrutura()
Long idGerado = bancoDeDados.insere(u);
u.setId(idGerado);
return u;
}
}
44. new… Vs Injeção de Dependência
class CadastroUsuarioTest {
CadastroUsuario cadastro = ...;
deveInserirNovoUsuarioNoBanco() {
Usuario u = new Usuario()
//u.set... com valores bem comportados..
cadastro.setBancoDados(...// banco de dados de teste em memória)
Usuario salvo = cadastro.salvaUsuario(u)
assertNotNull(salvo.getId())
}
}
45. new… Vs Injeção de Dependência
new = Forte Acoplamento
difícil manipulação para criação de cenários de teste
Principalmente classes de “infraestrutura” ou “dependência externa”
○ SGBDs, WebServices, Caches, Envio de Emails, etc.
Não crie! receba pronto e use.
46. Referências Estáticas Vs Instâncias e Interfaces
Cenário: contador de tempo
int contaAteHoje(Data dataNoPassado) {
Data hoje = DateUtils.hoje();
return diferencaEmDias(hoje, dataNoPassado);
}
47. Referências Estáticas Vs Instâncias e Interfaces
Fazendo um teste...
deveContarDiasAteHoje() {
Data dataNoPassado = 1/1/2016
int contagemReal = contadorDeDias.contaAteHoje(dataNoPassado)
int contagemEsperada = 20;
// O teste só vai passar se executado em 21/1/2016….
assertEquals(contagemReal, contagemEsperada)
}
48. Referências Estáticas Vs Instâncias e Interfaces
Viabilizando o teste...
Relogio relogio ...// Recebe como Injeção de Dependência
int contaAteHoje(Data dataNoPassado) {
Data hoje = relogio.hoje();
return diferencaEmDias(hoje, dataNoPassado);
}
49. Referências Estáticas Vs Instâncias e Interfaces
Viabilizando o teste
deveContarDiasAteHoje() {
Data dataNoPassado = 1/1/2016
Data hojeFake = 21/1/2016
Relogio mockRelogio = // Mock de Relogio onde hoje() retorna ‘hojeFake’
contadorDeDias.setRelogio(mockRelogio)
int contagemReal = contadorDeDias.contaAteHoje(dataNoPassado)
int contagemEsperada = 20;
// O teste vai passar qualquer dia...
assertEquals(contagemReal, contagemEsperada)
}
50. Referências Estáticas Vs Instâncias e Interfaces
Pontos de atenção
Utils.algoQueTodoMundoUsa()
Por que não objeto.algoQueTodoMundoUsa() ?
Isolar com interface
hoje() { return Calendar.getInstance().getTime()) }
Similar com Integrações Externas (SGBDs, WebServices,etc..)
52. Dado o código abaixo...
Usuario salvaUsuario(Usuario u) {
ControleDeAcesso ca = //cria e configura Controle de Acesso
OperacaoControleAcesso o = ca.criaOperacaoVerificaPermissaoSalvarUsuario();
temPermissao = o.executa()
if(temPermissao) {
TransacaoBancoDados tx = bancoDados.criaTransacao();
tx.begin()
bancoDeDados.insere(u)
tx.commit();
EnviadoDeEmail enviador = //recupera enviador de email
Email email = enviador.novoEmail()
email.// configura email a enviar..
email.envia();
Cache cache = // cria e configura Cache
if(cache.estaNoCache(u)) {
cache.invalidaCache(u)
}
}
53. … O que precisamos fazer para...
… Criar cenário de teste para testar:
Se a função persiste o usuário no banco?
Se um rollback é disparado, caso ocorra erro?
Se, somente pessoas autorizadas podem invocar a função?
Se um email é enviado corretamente após usuário inserido?
Se a lógica de Cache está correta?
Para testarmos um aspecto do Cadastro de Usuário
(persistência, controle de transação, segurança, envio
de email) temos que nos preocupar com todos os
outros...
57. Automatize tudo que puder
Sua bateria de testes tende a crescer…
Gaste tempo (muito) pensando em automação de tarefas
Abuse das ferramentas disponíveis
Identifique tarefas rotineiras e repetitivas