• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Dekoratory - jak wyeliminować powtórzenia? Reguła DRY

VMware Cloud PRO - przenieś swoją infrastrukturę IT do chmury
0 głosów
165 wizyt
pytanie zadane 3 czerwca 2023 w Python przez whiteman808 Gaduła (4,720 p.)

Witam

Wie ktoś jak w tym kodzie wyeliminować powtórzenia kodu dekoratorów?

Pozdrawiam

import pytest
from example_shop.shop.money import Currency, Money
from decimal import Decimal
from typing import Optional


def money_euro(amount: str, precision: Optional[int] = None) -> Money:
    if precision is None:
        return Money(Decimal(amount), Currency("Euro", "EUR", Decimal("4.52")))
    return Money(Decimal(amount), Currency("Euro", "EUR", Decimal("4.52")), precision)


def money_usd(amount: str, precision: Optional[int] = None) -> Money:
    if precision is None:
        return Money(Decimal(amount), Currency("American dollar", "USD", Decimal("4.17")))
    return Money(Decimal(amount), Currency("American dollar", "USD", Decimal("4.17")), precision)


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("2.7")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("4.0")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("4.00000")),
                          (money_usd("1.5", 4), money_usd("1.5", 3), money_usd("3", 4)),
                          (money_usd("-1.5", 4), money_usd("3", 5), money_usd("1.5", 5))])
def test_money_add_the_same_currency(price1, price2, expected):
    assert price1 + price2 == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("0.3")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("1.4")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("1.40000")),
                          (money_usd("1.5", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("3", 5), money_usd("-1.5", 5))])
def test_money_subtract_the_same_currency(price1, price2, expected):
    assert price1 - price2 == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("1.8")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("3.51")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("3.51000")),
                          (money_usd("0", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("-3", 5), money_usd("-4.5", 5))])
def test_money_multiply_the_same_currency(price1, price2, expected):
    assert price1 * price2 == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("1.25")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("2.08")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("2.08000")),
                          (money_usd("0", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("-3", 5), money_usd("-0.5", 5))])
def test_money_divide_the_same_currency(price1, price2, expected):
    assert price1 / price2 == expected


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_add_the_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 + price2


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_subtract_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 - price2


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_multiply_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 * price2


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_divide_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 / price2


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("2.7")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("4.0")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("4.00000")),
                          (money_usd("1.5", 4), money_usd("1.5", 3), money_usd("3", 4)),
                          (money_usd("-1.5", 4), money_usd("3", 5), money_usd("1.5", 5))])
def test_money_add_in_place_the_same_currency(price1, price2, expected):
    result = price1
    result += price2
    assert result == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("0.3")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("1.4")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("1.40000")),
                          (money_usd("1.5", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("3", 5), money_usd("-1.5", 5))])
def test_money_subtract_in_place_the_same_currency(price1, price2, expected):
    result = price1
    result -= price2
    assert result == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("1.8")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("3.51")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("3.51000")),
                          (money_usd("0", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("-3", 5), money_usd("-4.5", 5))])
def test_money_multiply_in_place_the_same_currency(price1, price2, expected):
    result = price1
    result *= price2
    assert result == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("1.25")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("2.08")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("2.08000")),
                          (money_usd("0", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("-3", 5), money_usd("-0.5", 5))])
def test_money_divide_in_place_the_same_currency(price1, price2, expected):
    result = price1
    result /= price2
    assert result == expected


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_add_in_place_the_different_currency(price1, price2):
    with pytest.raises(ValueError):
        result = price1
        result += price2
        assert result


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_subtract_in_place_different_currency(price1, price2):
    with pytest.raises(ValueError):
        result = price1
        result -= price2
        assert result


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_multiply_in_place_different_currency(price1, price2):
    with pytest.raises(ValueError):
        result = price1
        result *= price2
        assert result


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_divide_in_place_different_currency(price1, price2):
    with pytest.raises(ValueError):
        result = price1
        result /= price2
        assert result

 

1 odpowiedź

0 głosów
odpowiedź 3 czerwca 2023 przez Gynvael Coldwind Nałogowiec (30,510 p.)

W testach o DRY bym się specjalnie nie martwił. Testy to dość specyficzny kod, więc różne zasady mają trochę mniejsze zastosowanie.

Drugi problem jest taki, że w większości testów masz jakieś spodziewane wyniki, które będą różne, więc nie do końca da się wrzucić parametry testów w stałe. Można zrobić funkcję, która wypełnia "szablon" parametrów i go zwraca, ale kod traci trochę na czytelności.

# jakis tam podobny dekorator do testow
def decosink(a, b):
  def somedeco(f):
    def lastdeco():
      print("params:", a, b)
      f()
    return lastdeco
  return somedeco

# parametry jako "stała"
DECO_PARAMS = "ala", ["ma", "kota"]

# parametry tworzone szablonem
def make_params(expected_values):
  return "ala", ["ma", expected_values]

DECO_PARAMS_SET_2 = make_params("psa")
DECO_PARAMS_SET_3 = make_params("chomika")

@decosink(*DECO_PARAMS)
def costam():
  print("costam")

@decosink(*DECO_PARAMS_SET_2)
def costam_innego():
  print("costam_innnego")

@decosink(*DECO_PARAMS_SET_3)
def costam_jeszcze():
  print("costam_jeszcze")

costam()
costam_innego()
costam_jeszcze()

 

 

 

komentarz 3 czerwca 2023 przez whiteman808 Gaduła (4,720 p.)
przywrócone 3 czerwca 2023 przez whiteman808
Jest sens w przypadku tej klasy robić w ogóle jakiekolwiek testy?
komentarz 4 czerwca 2023 przez Gynvael Coldwind Nałogowiec (30,510 p.)
"Money" brzmi jak klasa, która zdecydowanie potrzebuje testów, bo jest krytyczna od strony biznesowej.

Ba, pewnie bym nawet dodał fuzztesty porównujące wyniki z jakąś inną implementacją.

Plus masę skrajnych wartości, żeby się upewnić, że żadnych floatów tam nie ma po drodzę.

To może być na wagę złota jeśli chodzi o future proofing też – a nuż za 5 lat przyjdzie jakiś nowy programista i stwierdzi, że aaa ta klasa Money to wszystko zwalnia bo ma wolną implementację, i zmieni jej działanie wprowadzając subtelne błędy. Dobre testy to wyłapią.

No i historycznie było trochę problemów z tego typu klasami.

Podobne pytania

0 głosów
0 odpowiedzi 436 wizyt
0 głosów
2 odpowiedzi 3,606 wizyt
pytanie zadane 28 października 2018 w Python przez Eliro Stary wyjadacz (12,160 p.)
+1 głos
2 odpowiedzi 517 wizyt
pytanie zadane 7 listopada 2019 w Python przez Kamil Początkujący (430 p.)

93,437 zapytań

142,431 odpowiedzi

322,671 komentarzy

62,802 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

...