-
-
Save rafaelpontezup/ecba938670eb13f876bd279e898b84af to your computer and use it in GitHub Desktop.
package br.com.zup.edu.certificacoes.testing.parcelador; | |
import java.math.BigDecimal; | |
import java.math.RoundingMode; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class ParceladorDeFatura { | |
public static final BigDecimal VALOR_MINIMO_FATURA = new BigDecimal("100"); | |
public static final BigDecimal VALOR_MAXIMO_FATURA = new BigDecimal("100000"); // UPDATED at 2024-10-24 | |
public List<BigDecimal> parcela(BigDecimal valor, int quantidadeDeParcelas) { | |
if (valor == null) { | |
throw new FaturaInvalidaException("Valor da fatura não pode ser nula"); | |
} | |
if (valor.compareTo(VALOR_MINIMO_FATURA) < 0) { | |
throw new FaturaInvalidaException("Valor da fatura muito baixo para parcelamento"); | |
} | |
// UPDATED at 2024-10-24 | |
if (valor.compareTo(VALOR_MAXIMO_FATURA) > 0) { | |
throw new FaturaInvalidaException("Valor da fatura acima do valor máximo para parcelamento"); | |
} | |
if (quantidadeDeParcelas < 2 || quantidadeDeParcelas > 10) { | |
throw new FaturaInvalidaException("Número de parcelas deve estar entre 2 e 10"); | |
} | |
BigDecimal numeroDeParcelas = BigDecimal.valueOf(quantidadeDeParcelas); | |
BigDecimal valorDaParcela = valor.divide(numeroDeParcelas, 2, RoundingMode.DOWN); | |
List<BigDecimal> parcelas = new ArrayList<>(); | |
for (int i = 0; i < quantidadeDeParcelas - 1; i++) { | |
parcelas.add(valorDaParcela); | |
} | |
// adiciona resto na ultima parcela | |
BigDecimal resto = valor.subtract(valorDaParcela.multiply(numeroDeParcelas)); | |
parcelas.add(valorDaParcela.add(resto)); | |
return parcelas; | |
} | |
} | |
Cenário 1: Valor da Fatura ser nulo
Cenário 2: Valor a ser pago ser menor que o valor mínimo
Cenário 3: Quantidade de parcelas ser menor que 2
Cenário 4: Quantidade de parcelas ser maior que 10
Cenário 5: Happy path para quando tem resto e quando não tem resto
Eu escreveria os seguintes cenários:
- O valor da parcela não deve ser nulo
- O valor da parcela não deve ser inferior ao valor mínimo da fatura.
- A quantidade de parcelas não deve ser inferior a 2 ou superior a 10 ( com varias entradas)
- Deve parcelar um valor acima do valor mínimo de fatura, com parcelas entre 2 e 10
- Deve parcelar um valor onde o resto seja diferente de zero
- Valor da fatura nulo
- Valor muito baixo para parcelamento (um teste com valor menor que o mínimo e um com o mínimo valor - boundary)
- quantidadeDeParcelas 1, 2, 10, 11 (boundary)
- MCDC if (3 casos, já cobertos no item 3)
- for - não gera coleção vazia (já coberto no item 3)
- for - não gera coleção com um elemento (ou seja, parcela única)
- Parcelamento com resto zero e maior que zero
Acho que somando tudo aqui daria 11 casos, mínimo (talvez eu fizesse mais com mesmo cenário mas asserts diferentes, onde eles significariam coisas diferentes).
Enxergo pelo menos 6 cenários:
- 4 para exceções: valor nulo da fatura, valor de fatura menor que o mínimo e os limites das quantidades de fatura.
- 2 para checar os valores das parcelas, quando o valor da fatura é dividível pelo número de parcelas e os valores da parcela são iguais e o outro quando não é dividível e apenas o último valore da fatura é diferente.
Exemplos:
- Para verificar se com o valor da fatura nulo, é acionado uma exceção de fatura invalida pelo valor nulo.
- Para verificar se com o valor da fatura menor que o mínimo, é acionado uma exceção de fatura invalida para parcelamento.
- Para verificar se com a quantidade de parcela menor que 2, é acionado uma exceção de fatura invalida para a quantidade de parcela.
- Para verificar se com a quantidade de parcela maior que 10, é acionado uma exceção de fatura invalida para a quantidade de parcela.
- Para verificar se com a quantidade de parcela igual a 2 com um valor dividível por 2, checar que as parcelas estão com o mesmo valor.
- Para verificar se com a quantidade de parcela igual a 10 com um valor não dividível por 10, e checar com a exceção da última se que as parcelas estão com o mesmo valor.
- Valor nulo
- Valor negativo
- Valor da fatura baixo para o parcelamento
- Quantidade de parcelas abaixo de 2
- Quantidade de parcelas maior que 10
- Quantidade de parcelas dentro do valor esperado
- Valor e parcelas que desse divisão exata e tudo certo
- Valor e parcelas que desse quebrado e gerasse resto
valor
nulo => Exceptionvalor
menor que o mínimo => Exceptionvalor
maior que mínimo equantidadeDeParcelas
com 1 => Exceptionvalor
maior que mínimo equantidadeDeParcelas
com 1 => Exceptionvalor
maior que mínimo equantidadeDeParcelas
com 11 => Exceptionvalor
igual ao mínimo equantidadeDeParcelas
com 2 e sem resto => verifica listvalor
maior que mínimo equantidadeDeParcelas
com 2 e sem resto => verifica listvalor
maior que mínimo equantidadeDeParcelas
com 10 e sem resto => verifica listvalor
maior que mínimo equantidadeDeParcelas
entre 2 e 10 e sem resto => verifica listvalor
maior que mínimo equantidadeDeParcelas
entre 2 e 10 e com resto => verifica list
- Entrou no primeiro if (valor nulo)
- Entrou no segundo if (valor anterior ao minimo)
- Entrou no terceiro if pelo lado esquerdo (quantidade = 1)
- Entrou no terceiro if pelo lado direito (quantidade = 11)
- Entrou no loop uma vez(quantidade = 2)
- Entrou no loop rodando duas ou mais vez (quantidade = 10)
- Self testing no final para verificar que tem "quantidade" no array
Aí poderia exercitar mais vezes
Eu enxerguei 6 cenários/casos de testes:
- Entrou no primeiro
if
(valor nulo); - Entrou no segundo
if
(valor menor que 100); - Entrou no terceiro
if
pelo lado esquerdo (quantidade = 1); - Entrou no terceiro
if
pelo lado direito (quantidade = 11); - Happy-path com valor redondo (valor = 100 e quantidade = 2);
- Happy-path com valor quebrado gerando resto (valor = 100 e quantidade = 3);
Contudo....
Podemos resumir em 4 cenários/casos se considerarmos classes de equivalência, ou seja, podemos juntar na mesma classe itens 3 e 4, e também itens 5 e 6.
A implementação do método é bem direta. Meu primeiro foco seria testar cada um dos caminhos possíveis nesse método, que são bem claros se vc olhar os ifs do código. Aproveito e sempre que visito um if, olho pro valor de entrada do usuário que ele usa, e analiso o domínio de entrada daquele valor:
- Linha 14: Valor nulo, teste espera uma exceção
- Linha 18: Valor menor que 0, teste espera uma exceção, e valor igual a 0 (fronteira da condição), programa não quebra (Não quebra mesmo, me parece estranho parcelar 0 reais...)
- Linha 18: Me chama a atenção o fato de não ter limite superior para valor. Se esse for mesmo o caso, eu testaria com um valor bem alto (MAX_INT) pra garantir que tudo continua funcionando.
- Linha 22: Quantidade de parcela igual a 2 e igual a 1 (1 não funciona, 2 funciona). Dois testes aqui pra exercitar a fronteira da condição.
- Linha 22: Quantidade de parcela igual a 10 e igual a 11 (10 funciona, 11 não funciona). Dois testes aqui pra exercitar a fronteira.
A partir daqui o método tem um caminho único. Olhando a implementação, entendo que o código divide o total entre o número de parcelas pedido, adiciona qualquer diferença na última parcela. Regras de arredondamento são duas casas decimais e arredondar pra baixo.
Pensando nas principais variáveis que estão em jogo aqui:
- Última parcela: Última parcela que seja um pouco maior do que as anteriores, e última parcela que seja igual as anteriores. Pensaria aqui também se é possível que a última parcela seja um pouco menor, mas como arredondamos pra baixo, acho que não é possível.
- Arredondamento: caso onde o número seja forçado a arredondar pra baixo
- Número de parcelas: 2, 10 (limites), um número no meio ("in point") qualquer apenas pra garantir.
Não consigo pensar em mais nada. Agora é só transformar esses casos de teste em testes automatizados. Se rodar cobertura de código aqui, espero que bata 100%. Se não bater, olhar o que faltou.
Esse método é um excelente caso também para property-based tests. A propriedade é direta, e bem fácil de ser implementada: dado qualquer número de parcelas entre 2 e 10, e qualquer valor maior do que zero, a soma das parcelas geradas tem que ser igual ao valor inicial.
PS: Vc não precisa de testes repetidos. O teste com 2 parcelas que sugeri no começo pode ser o mesmo teste que sugeri quando explorei o "caminho feliz" do método. Quando estou pensando nos casos de testes, não me preocupo muito em encontrar "o menor conjunto de testes", a limpeza acontece depois.
A : BigDecimal
B : Int
1. A == null ==> Exception
2. A < 100 ==> Exception
3. B < 2 ==> Exception
4. B > 10 ==> Exception
5. Para todo (A : BigDecimal) e (B : Int), onde A!= null, e A >= 100, e B >= 2, e B <= 10,
somaLista(parcela(A,B)) == A
length(parcela(A,B)) == B
Confesso que tenho dificuldades para bater o olho e dizer quantos, geralmente vou fazendo descobrindo e tentando cobrir todas as possiblidades.
1 - para o primeiro if
3 - para o segundo if ( com numero acima de 0, com 0 e com menor que 0)
6 - para o terceuri if( parecido com o de cima mas com duas condicionais OR)
Alguns para o valor, não sei exatamente quantos nesse caso mas pelo menos 3 por conta do arredondamento e valores. talvez testes que explorem valores aleatórios pra ajudar a descobrir bugs, tem uma ferramenta pra isso mas esqueci o nome xD
Mas acho que pelo menos 13 kkkkk