package prácticaunidad3;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
/**
* Realmente nos deberían dar os requisitos do sistema e o a información do API (os métodos)
* a implementar, Por exemplo, en este caso nos terían que decir:
*
* Debemos facer un Test JUnit que nos permita verificar o ingreso e retirada de fondos dunha
* Conta Corrente.
*
* O ingreso ten que ser unha cantidade positiva. Si se intenta ingresar unha cantidade negativa, se
* lanzará un erro (exception) co texto "No se puede ingresar una cantidad negativa"
* O saldo quedará incrementado na cantidade ingresada.
*
* A retirada ten que indicarse cunha cantidade positiva. Si se intenta retirar unha cantidade negativa
* se lanzará un erro (exception) co texto "No se puede ingresar una cantidad negativa". Si se
* intentan retirar máis cartos dos dispoñibles no saldo se debe lanzar un erro (exception) co texto
* "No existe suficiente saldo". O saldo quedará decrementado na cantidade retirada.
*
* A clase CCuenta dispón dos métodos:
*
* public CCuenta () --> Constructor por defecto. Crea un CCuenta con saldo 0
* public CCuenta (String nom, String cue, double sal, double tipo) --> Constructor inicializando os atributos da clase
* public void asignarNombre(String nom) --> Pon nome ao titular da conta
* public String obtenerNombre() --> Devolve o nome do titular da conta
* public double estado () --> Devolve o saldo da conta
* public void ingresar(double cantidad) throws Exception --> Permite ingresar unha cantidade da conta
* public void retirar (double cantidad) throws Exception --> Permite retirar unha cantidade da conta
* public String obtenerCuenta () --> Permite obter o número de conta
*
*
* A partir de aquí deseñaríamos os casos de proba para o ingreso e a retirada.
* O deseño dun caso de uso consiste simplemente en elexir uns datos de entrada o suficientemente
* representativos para garantizar o correcto funcionamento da clase (para o que se poden empregar
* distintos métodos vistos na unidade, como valores límite) e calcular os resultados que
* se deben obter.
* A codificación dos casos de test, consiste na escritura dun pequeno programa ou varios programas
* pequenos que subministren esas entradas e comproben si o resultado é correcto. Si o resultado
* devolto non é correcto é que a unidade (clase) que estamos probando non cumple os requisitos
* funcionais, polo que non é correcta.
*
* JUnit non é máis que un "framework" que nos facilita a construción deses programas de proba
* e proporciona un xeito estándar de facelo, facilitando a reutilización, documentación e sistematización
* dos tests de proba.
*
* Neste caso:
* --> Test de Ingresar
* Caso 1: O saldo é 0 e ingresamos unha cantidade negativa
* Debe lanzarse a excepción co texto "No se puede ingresar una cantidad negativa"
* Caso 2: O saldo é 100.750 e ingresamos 3000.25
* O saldo resultante debe ser 3101
* Caso 4: O saldo é 2147483640 (cerca do límite de int) e ingreso 300
* O saldo resultante debe ser 2147483940
* Caso 5: O saldo é de 9007199254740992 (busco o límite enteiro dun double) e ingreso 1
* Se debe lanzar unha excepción co texto "No se puede exceder el saldo máximo"
*
* ---> Test de Retirar
* Caso 1: Retiro 150 euros dunha conta con saldo 100
* Debe lanzarse unha excepción co texto "No existe suficiente saldo"
* Caso 2: Retiro -150 euros dunha conta con saldo 100
* Debe lanzarse unha excepción co texto "No se puede retirar una cantidad negativa"
* Caso 3: Retiro 135.75 euros dunha conta con saldo 136
* O saldo resultante debe ser 0.25
* ---> Ingresar e Retirar
* Caso : Partimos con saldo 100.234, ingresamos 100.200 e retiramos 140.034
* Debe quedar un saldo de 60.40
*
* Este sería o deseño dos test de unidade. Agora os codificaremos usando o "framework"
* de testing JUnit (existen outros).
* A verificación das excepcións se pode facer de tres modos:
* a) Capturando a excepción
* b) Mediante unha anotación (non permite verificar o texto da excepción)
* c) Mediante unha regla JUnit (Rule)
*
* Fixádevos que si o test falla, non é porque o test de unidade esté mal nin porque
* esté mal codificado en JUnit, é porque a clase CCuenta.java NON E CORRECTA.
*
* Si o saldo é demasiado grande non se comporta como debe.
*
* Para probar podedes arranxar CCuenta.java engadindo esto ao principio do método
* ingresar:
* if ((saldo>0)&&(saldo+cantidad == saldo)) throw new Exception("No se puede exceder el saldo máximo");
*
* @author dessinweb
*/
public class CCuentaTest {
CCuenta conta;
public CCuentaTest() {
System.out.println("Constructor. Execútase unha vez ao comenzo do test");
}
@BeforeClass
public static void setUpClass() {
System.out.println("METODO ANOTADO COMO BeforeClass. (o nome do método non importa)");
System.out.println("BeforeClass. Se executa UNHA VEZ antes de crear a o obxecto CCuentaTest");
System.out.println("BeforeClass. Sirve para tarefas de inicialización");
}
@AfterClass
public static void tearDownClass() {
System.out.println("METODO ANOTADO COMO AfterClass. (o nome do método non importa)");
System.out.println("AfterClass: Se executa UNHA VEZ despois de todos os tests");
System.out.println("AfterClass: Sirve para tarefas de limpeza");
}
@Before
public void setUp() {
System.out.println("METODO ANOTADO COMO Before. (o nome do método non importa)");
System.out.println("Before: Se executa antes de CADA test");
System.out.println("Before. Sirve para tarefas de inicialización");
// Antes de cada test creamos unha conta con saldo 0, para non ter influencia
// de un test en outro. Recordemos que os test se executan en un orde aleatorio
conta=new CCuenta("Cuenta Test","NumCuenta",0.0,0.0);
}
@After
public void tearDown() {
System.out.println("METODO ANOTADO COMO After. (o nome do método non importa)");
System.out.println("After: Se executa despois de cada test");
System.out.println("After: Sirve para tarefas de limpeza");
}
/** TEST 1: INGRESO
* Comprobo a excepción cunha anotación... non podo verificar a mensaxe,
* únicamente que a Exception se lanza... é útil si se lanzara unha Exception moi
* específica na que non fora relevante a mensaxe. Ademáis este método é pouco
* apropiado a esta situación, xa que lle sirven as Exception que lanzan os
* assert fallidos, dando o test como pasado incorrectamente.
* Outro problema, é que unha vez que se lanza a Exception non se continúan
* verificando o resto de asserts, polo que o temos que separar en dous métodos.
*
* Caso 1: O saldo é 0 e ingresamos unha cantidade negativa
* Debe lanzarse a excepción co texto "No se puede ingresar una cantidad negativa"
* Caso 2: O saldo é 100.750 e ingresamos 3000.25
* O saldo resultante debe ser 3101
* Caso 4: O saldo é 2147483650 (cerca do límite de int) e ingreso 300
* O saldo resultante debe ser 2147483950
* Caso 5: O saldo é de 9007199254740992 (busco o límite enteiro dun double) e ingreso 1
* Debería lanzar un erro co texto "No se puede exceder el saldo máximo"
*/
@Test(expected=Exception.class)
public void testIngresoNegativoWithAnnotatedExceptionExample() throws Exception {
conta.ingresar(-100);
}
@Test
public void testIngresoAnnotatedExceptionExample() throws Exception {
// Non teño un método para cambiar o saldo, teño que crear unha nova conta
conta=new CCuenta("Cuenta Test","NumCuenta",100.750,0.0);
conta.ingresar(3000.25);
assertEquals("O saldo debe ser 3101",3101.00,conta.estado(),0.0);
conta=new CCuenta("Cuenta Test","NumCuenta",2147483650.0,0.0);
conta.ingresar(300);
assertEquals("O saldo debe ser 2147483950",2147483950.0,conta.estado(),0.0);
}
@Test(expected=Exception.class)
public void testIngresoOverflowWithAnnotatedExceptionExample() throws Exception {
conta=new CCuenta("Cuenta Test","NumCuenta",9007199254740992.0,0.0);
conta.ingresar(1);
}
/**
* E o mesmo test que nos tres métodos anteriores, pero capturando
* a Exception. Podemos verificar a mensaxe e non temos os problemas
* indicados anteriormente.
*/
@Test
public void testIngresoWithCapturedExceptionExample() throws Exception {
try {
conta.ingresar(-100);
fail("Se debería lanzar un error por ingresar una cantidad negativa");
} catch(Exception e) {
// Comprobamos o texto da exception
if (!e.getMessage().equals("No se puede ingresar una cantidad negativa")) {
fail("A Exception obtenida: "+e.getMessage()+", non corresponde coa desexada");
}
}
conta=new CCuenta("Cuenta Test","NumCuenta",100.750,0.0);
conta.ingresar(3000.25);
assertEquals("O saldo debe ser 3101",3101.00,conta.estado(),0.0);
conta=new CCuenta("Cuenta Test","NumCuenta",2147483650.0,0.0);
conta.ingresar(300);
assertEquals("O saldo debe ser 2147483950",2147483950.0,conta.estado(),0.0);
try {
conta=new CCuenta("Cuenta Test","NumCuenta",9007199254740992.0,0.0);
conta.ingresar(1);
fail("Se debería lanzar un error por exceder el límite de saldo");
} catch(Exception e) {
// Comprobamos o texto da exception
if (!e.getMessage().equals("No se puede exceder el saldo máximo")) {
fail("A Exception obtenida: "+e.getMessage()+", non corresponde coa desexada");
}
}
}
/**
* E o mesmo test que os anteriores, pero usando unha Rule para comprobar
* a Exception. Podemos verificar a mensaxe e non temos os problemas
* indicados anteriormente.
* PERO, so podemos controlar o lanzamento de unha exception por test
* debemos separalo en dous.
*/
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Test
public void testIngresoNegativoWithRuleExceptionExample() throws Exception {
expectedEx.expect(Exception.class);
expectedEx.expectMessage("No se puede ingresar una cantidad negativa");
conta.ingresar(-100);
}
/**
* Necesito dividir o test en dous, xa que unha vez que se lanza a Exception
* o codigo de debaixo nunca se chega a executar
* @throws Exception
*/
@Test
public void testIngresoWithRuleExceptionExample() throws Exception {
conta=new CCuenta("Cuenta Test","NumCuenta",100.750,0.0);
conta.ingresar(3000.25);
assertEquals("O saldo debe ser 3101",3101.00,conta.estado(),0.0);
conta=new CCuenta("Cuenta Test","NumCuenta",2147483650.0,0.0);
conta.ingresar(300);
assertEquals("O saldo debe ser 2147483950",2147483950.0,conta.estado(),0.0);
}
@Test
public void testIngresoOverflowWithRuleExceptionExample() throws Exception {
expectedEx.expect(Exception.class);
expectedEx.expectMessage("No se puede exceder el saldo máximo");
conta=new CCuenta("Cuenta Test","NumCuenta",9007199254740992.0,0.0);
conta.ingresar(1);
}
/**
* Test de Retirar.
* Caso 1: Retiro 150 euros dunha conta con saldo 100
* Debe lanzarse unha excepción co texto "No existe suficiente saldo"
* Caso 2: Retiro -150 euros dunha conta con saldo 100
* Debe lanzarse unha excepción co texto "No se puede retirar una cantidad negativa"
* Caso 3: Retiro 135.75 euros dunha conta con saldo 136
* O saldo resultante debe ser 0.25
*/
@Test
public void testRetirada() throws Exception {
// Retirada Excesiva
try {
conta=new CCuenta("Cuenta Test","NumCuenta",100.0,0.0);
conta.retirar(150);
fail("Debe lanzarse una Exception con el texto 'No existe suficiente saldo'");
} catch(Exception e) {
if (!e.getMessage().equals("No existe suficiente saldo"))
fail("A Excepción lanzada non é a apropiada");
}
// Retirada Negativa
try {
conta.retirar(-150);
fail("Debe lanzarse una Exception con el texto 'No se puede retirar una cantidad negativa'");
} catch(Exception e) {
if (!e.getMessage().equals("No se puede retirar una cantidad negativa"))
fail("A Excepción lanzada non é a apropiada");
}
conta=new CCuenta("Cuenta Test","NumCuenta",136.0,0.0);
conta.retirar(135.75);
assertEquals("Test Retirada 136-135.25, Deben quedar 0.25",0.25,conta.estado(),0.0);
}
/**
* Test de Retirada e Ingreso
* ---> Ingresar e Retirar
* Caso : Partimos con saldo 100.234, ingresamos 100.200 e retiramos 140.034
* Debe quedar un saldo de 60.40
*/
@Test
public void testIngresoRetirada() throws Exception {
conta=new CCuenta("Cuenta Test","NumCuenta",100.234,0.0);
conta.ingresar(100.200);
conta.retirar(140.034);
// Como vemos, este assert falla. Se debe a falta de precisión de double
// xestionando os números enteiros, normalmente temos que indicar no último
// parámetro o erro "aceptable". O deberíamos facer en TODOS os assert anteriores..
//assertEquals("Deben quedar 60.40",60.40,conta.estado(),0.0);
// Este sería o assertEquals CORRECTO
assertEquals("Deben quedar 60.40",60.40,conta.estado(),0.00000000001);
}
}