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 33

Preguntas 4

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

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 鈥渄elta鈥, 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;
    }>

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