• 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

Object Storage Arubacloud
0 głosów
113 wizyt
pytanie zadane 3 czerwca 2023 w Python przez whiteman808 Obywatel (1,820 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 (27,530 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 Obywatel (1,820 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 (27,530 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 229 wizyt
0 głosów
2 odpowiedzi 3,115 wizyt
pytanie zadane 28 października 2018 w Python przez Eliro Stary wyjadacz (12,160 p.)
+1 głos
2 odpowiedzi 324 wizyt
pytanie zadane 7 listopada 2019 w Python przez Kamil Początkujący (430 p.)

92,573 zapytań

141,423 odpowiedzi

319,647 komentarzy

61,959 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.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...