No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Curso Básico de Testing en Java

Curso Básico de Testing en Java

Ferran Maylinch Carrasco

Ferran Maylinch Carrasco

Ejemplos de TDD: cálculo de descuentos

14/24
Recursos

Aportes 35

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

assertThat, actualmente esta deprecated. La forma de validar valores double es con assertEqueals(double expected, double actual, double delta). La funcion assertEquals(double expected, double actual) tambien está deprecated. Para los que no estén familiarizados con el valor “delta”, seria diciéndolo de una forma sencilla, _ el margen de error que vamos a permitir a la hora de ejecutar la igualdad_.
Por ejemplo: si comparamos 45.10 como valor esperado con un valor obtenido 45.19, si quieres un 100% de precisión, deberíamos pasar un delta con valor 0 (con esto estamos diciendo de que no vamos a permitir ningún valor por encima ni por debajo del valor esperado). Pero supongamos que estamos haciendo el test de una función que maneja grados y queremos permitir un margen de error de 0.10 grados. Este seria el valor que debemos pasar por delta, quedando nuestra assertEquals de la siguiente forma: assertEquals(45.10, 45.19, 0.10) este ejemplo pasaría el test y no nos daría error aunque el valor esperado y el valor obtenido sean distintos ya que estamos dentro del margen de error permitido.
Espero se entienda lo que quise explicar, me pareció interesante mencionar esto para que trabajemos fuera de métodos deprecated ya que son métodos destinados a desaparecer por varias razones que podemos verificar directamente en la api de referencia. Saludos a todos y que tengan un excelente día!

Me gusta más calcular el descuento de esta manera:

Tengo el metodo getTotal de la clase PriceCalculator refactorizado de la siguiente manera utilizando stream (necesario usar la version de Java 8 o superior)

public Double getTotal() {
    Double total = prices.stream().reduce(d, Double::sum);
    return total ((100 - discount) / 100);
}

Estuve haciendo mi código y me encontré con errores de precisión decimal, es decir, esperaba el valor 15.6 pero se devolvía 15.60000001. Después de hacer una investigación, se recomienda implementar operaciones con la clase BigDecimal para un control más preciso de los valores decimales, por ejemplo, manejo de operaciones con dinero. For more info https://dzone.com/articles/never-use-float-and-double-for-monetary-calculatio
.
BigDecimal tiene métodos para controlar la precisión decimal como por ejemplo setScale
.
Dejo aquí la implementación de mi clase con manejo de precisión de decimales
.

package jp.discounts;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class PriceCalculator {
    static private final int SCALE = 2;
    private final List<BigDecimal> list;
    private double discount;

    public PriceCalculator() {
        super();
        this.list = new ArrayList<>();
        this.discount = 0;
    }

    public double getTotal() {
        Optional<BigDecimal> total = this.list.stream().reduce(BigDecimal::add);
        
        return total.isPresent() ?
                total.get()
                        .multiply(new BigDecimal(1d - (discount / 100d)))
                        .setScale(SCALE, RoundingMode.HALF_EVEN)
                        .doubleValue()
                : 0d;
    }

    public void add(double v) {
        this.list.add(new BigDecimal(v));
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }
}

mi clase

package com.jose.javatest.discounts;

import java.util.ArrayList;
import java.util.List;

public class PriceCalculator {

    private List<Double> prices = new ArrayList<>();
    private double discount;

    public double getTotal() {
        double total = prices.stream().reduce(Double::sum).orElse(0.0);
        return total * ((100 - discount) /100);
    }

    public void addPrice(double price) {
        prices.add(price);
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }
}

Como yo lo hice:

public class PriceCalculator {
    List<Double> total = new ArrayList<Double>();
    double discount = 1;

    public double getTotal() {
        double totalAmount = 0;
        for (Double price : total) {
            totalAmount += price;
        }

        return totalAmount * discount;
    }

    public void addPrice(double price) {
        total.add(price);
    }

    public void setDiscount(int disc) {
        discount = disc * 0.01;
    }
}

Aplicando streams y Optional:

public class PriceCalculator {
    private List<Double> prices = new ArrayList<>();
    private double discount = 0.0;

    public double getTotal() {
        Optional<Double> sum = this.prices.stream().reduce((a, b) -> a + b);
        double total = sum.orElse(0.0) * (1- (this.discount/100));

        return total;
    }

    public void addPrice(double price) {
        this.prices.add(price);
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }
}

Creo que queda menos complejo sin ArrayList y usar un totalizador, así:

public class PriceCalculator {
    private double total = 0;

    public double getTotal() {
        return this.total;
    }

    public void addPrice(double price) {
        this.total += price;
    }

    public void setDiscount(double discount) {
        this.total -= this.total * (discount / 100);
    }
}

asi relice mi descuento, la vieja regla de tres simple.

public double getTotal() {
        double result = 0;
        for (Double price : prices) {
            result += price;
        }
        double finaltotal = result - ((result * discount) / 100);
        return finaltotal;
    }<code>
package com.platzi.javatests.discounts;

import java.util.ArrayList;
import java.util.List;

public class PriceCalculator {

    private List<Double> prices = new ArrayList<Double>();
    private double discount;

    public double getTotal() {
        double total = prices.stream().reduce(0.0, (subtotal, element) -> subtotal + element);
        double result = total * (1 - discount/100);
        return  result;
    }

    public void addPrice(double price) {
        prices.add(price);
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }
}

Comparto lo que hice

class CalculadoraDePreciosTest {

    CalculadoraDePrecios calculadora;
    @BeforeEach
    void setUp() {
        calculadora = new CalculadoraDePrecios();
    }

    @Test
    void total_zero_cuando_no_hay_precios() {
        assertEquals(0, calculadora.getTotal());
    }

    @Test
    void total_suma_valores_indicados() {
        calculadora.agregarValor(250.25);
        calculadora.agregarValor(150.99);
        calculadora.agregarValor(350.25);
        assertEquals(751.49, calculadora.getTotal());
    }

    @Test
    void aplicar_descuento() {
        calculadora.agregarValor(100);
        calculadora.agregarValor(50);
        calculadora.aplicarDescuento(10);
        assertEquals(135, calculadora.getTotal());
    }
} 
public class CalculadoraDePrecios {

    private double total;

    CalculadoraDePrecios() {
        this.total = 0;
    }
    public double getTotal() {
        return total;
    }

    public void agregarValor(double precio) {
        this.total += precio;
    }

    public void aplicarDescuento(double descuento) {
        this.total -= this.total * (descuento / 100);
    }
}
 

Excelente ejemplo! Se puede complementar agregando excepciones aritmeticas y creando tests que esperen dichas excepciones en algunos casos.

Mi clase

<    public double getTotal() {
       Double sum = prices.stream()
               .collect(Collectors
                       .summingDouble(Double::doubleValue));

       sum -= sum * (discount/100);

       return sum;
    }>
Mi aporte del total usando Iterator inteface `public double getTotal(){` ` double result = 0;` ` Iterator<Double> iterator = total.iterator();` ` while (iterator.hasNext()){` ` result += iterator.next();` ` }` ` return result * (100 - discount) / 100;` `}`
mi aporte: <https://github.com/Jeixonx/testingJava/commit/fe0a75f03543cc95a71462aa18eaafe59f04bf85>

para la clase PriceCalulator se puede dejar de la siguiente manera

package org.cr3ativos.discount;

import java.util.ArrayList;
import java.util.List;

public class PriceCalculator {
    List<Integer> prices = new ArrayList<>();

    public int getTotal() {
        return prices.stream().reduce(0, Integer::sum);
    }

    public void addPrice(int i) {
        prices.add(i);
    }
}

Codigo de la clase

Clase PriceCalculator

public class PriceCalculator {

    // lista precios
    private List<Double> prices = new ArrayList<>();
    private double discount = 0;

    public double getTotal() {

        //calcular la lista de los precios
        double result = 0;

        for (Double price : prices) {
            // suma
            result += price;
        }
        // resultado y aplicar descuento
        return result * ((100-discount)/100);
    }

    public void addPrice(double price) {
        prices.add(price);
    }

    public void setDiscount(double discount) {

        // campo discount
        this.discount = discount;
    }
}

Tests

public class PriceCalculatorShould{

    private PriceCalculator calculator;
    @Test
    public void total_zero_when_there_are_prices() {
        calculator = new PriceCalculator();

        assertThat(calculator.getTotal(),is(0.0));
    }

    @Test
    public void total_is_the_sum_of_prices() {
        calculator = new PriceCalculator();

        // total de precios para indicar al calculator
        calculator.addPrice(10.2);
        calculator.addPrice(15.5);

        assertThat(calculator.getTotal(),is(25.7));
    }

    @Test
    public void apply_discount_to_prices() {
        calculator = new PriceCalculator();

        // total de precios para indicar al calculator
        calculator.addPrice(12.5);
        calculator.addPrice(17.5);

        // aplicar descuento
        calculator.setDiscount(50);

        assertThat(calculator.getTotal(),is(15.0));
    }
}

TDD Es una belleza

Cuidado en los test si tienen alguna falla, verifica que estas usando números tipo double y no entero en el valor is(numero).

package com.platzi.market.web.discounts;

import java.util.ArrayList;
import java.util.List;

public class PriceCalculator {

    private List<Double> prices;
    private double discount;

    public PriceCalculator(){
        prices = new ArrayList<>();
    }

    public double getTotal() {
        return (prices.stream().mapToDouble(k -> k).sum())* (1- discount/100);
    }

    public void addPrice(double price) {
        prices.add(price);
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }
}

Copado!

Excelente ejemplo

que buen ejemplo !!!

PriceCalculator.java

package com.platzi.javatests.discounts;

import java.util.ArrayList;
import java.util.List;

public class PriceCalculator {

  private List<Double> prices = new ArrayList<>();
  private double discount;

  public double getTotal() {
    double result = 0;

    for (Double price : prices) {
      result += price;
    }

    return result * ((100 - discount) / 100);
  }

  public void addPrice(double price) {
    prices.add(price);
  }

  public void setDiscount(double discount) {
    this.discount = discount;
  }
}

PriceCalculatorShould.java

package com.platzi.javatests.discounts;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(MockitoJUnitRunner.class)
public class PriceCalculatorShould {

  @Mock
  PriceCalculator calculator;

  @Before
  public void setUp() {
    calculator = new PriceCalculator();
  }

  @Test
  public void totalZero_whenThereAreNotPrices() {
    assertThat(calculator.getTotal(), is(0.0));
  }

  @Test
  public void totalIsTheSumOfThePrices() {
    calculator.addPrice(10.2);
    calculator.addPrice(15.5);

    assertThat(calculator.getTotal(), is(25.7));
  }

  @Test
  public void applyDiscountToPrices() {
    calculator.addPrice(100);
    calculator.addPrice(50);
    calculator.addPrice(50);

    calculator.setDiscount(25);

    assertThat(calculator.getTotal(), is(150.0));
  }
}

Buen ejemplo de como aplicar el TDD, y como llevarlo a cabo excelente ejemplo, además requiere de hacer una buena programación para hacerlo de manera simple y que hace que los requerimientos comiencen a funcionar y luego da la tranquilidad para refactorizar el código

Con Java 8

package mx.gob.fgjgto.javaTesting.discounts;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class PriceCalculator {

    private List<Double> prices = new ArrayList<>();
    private double discount = 0;

    public double getTotal() {
        double result = 0.0;
        result = prices.stream().collect(Collectors.summingDouble(i -> i));
        return result * ((100 -discount) / 100);
    }

    public void addPrice(double price) {
        prices.add(price);
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }
}
package mx.gob.fgjgto.javaTesting.discounts;

import org.junit.Test;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

public class PriceCalculatorShould {

    @Test
    public void total_zero_when_there_are_prices() {
        PriceCalculator calculator = new PriceCalculator();
        assertThat(calculator.getTotal(), is(0.0));
    }

    @Test
    public void total_is_the_sum_of_prices() {
        PriceCalculator calculator = new PriceCalculator();
        calculator.addPrice(10.2);
        calculator.addPrice(15.5);
        assertThat(calculator.getTotal(), is(25.7));
    }

    @Test
    public void apply_discount_to_price_50() {
        PriceCalculator calculator = new PriceCalculator();
        calculator.addPrice(12.5);
        calculator.addPrice(17.5);
        calculator.setDiscount(50);
        assertThat(calculator.getTotal(), is(15.0));
    }

    @Test
    public void apply_discount_to_price_25() {
        PriceCalculator calculator = new PriceCalculator();
        calculator.addPrice(100);
        calculator.addPrice(50);
        calculator.addPrice(50);
        calculator.setDiscount(25);
        assertThat(calculator.getTotal(), is(150.0));
    }
}

Buena clase, todo muy claro.

import java.util.ArrayList;
import java.util.List;

public class PriceCalculator
{
    private List<Double> prices = new ArrayList<>();
    private double discount = 0;


    public double getTotal()
    {
        double totalPrice = 0;

        for (double price : prices)
        {
            totalPrice += price;
        }
        return totalPrice * ((100-discount) / 100);
    }


    public void addPrice(double price)
    {
        prices.add(price);
    }


    public void applyDiscount(double discount)
    {
        this.discount = discount;
    }
}
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;


public class PriceCalculatorShould
{
    private PriceCalculator calcular;

    @Before
    public void setup()
    {
        calcular = new PriceCalculator();
    }

    @Test
    public void return_zero_when_there_arenot_prices()
    {
        assertThat(calcular.getTotal(), is(0.0));
    }

    @Test
    public void return_total_the_sum_of_prices()
    {
        calcular.addPrice(1200.0);
        calcular.addPrice(1200.0);

        assertThat(calcular.getTotal(),is(2400.0));
    }

    @Test
    public void apply_discount_to_prices()
    {
        calcular.addPrice(2000.0);
        calcular.addPrice(1500.0);
        calcular.addPrice(2500.0);

        calcular.applyDiscount(50);

        assertThat(calcular.getTotal(),is(3000.0));

    }
}

Excelente clase instructor Ferran, al principio me pareció un poco extraño que fallara al colocar el descuento solo, pero note que la prueba que fallaba era la que no lo poseía entonces me di cuenta que según la estructura inicial del código si no hay un descuento el resultado se multiplicaría por cero y nos daría ese mismo valor, por esa razón después lo corrigió el instructor Ferran.

Super claro 😃

Gracias 😃

me gusta la forma como el IDE asiste el código, es muy eficaz.

Muy interesante

En estos casos podría ser necesario añadir algunas validaciones extras como aproximar el valor en caso de que hayan resultados como 28.199999999996 y el test lo tenemos como 28.2