Testowanie jednostkowe potoków przetwarzania danych składających się z funkcji jednowierszowych

10

Czytając praktyczne wprowadzenie Mary Rose Cook do programowania funkcjonalnego , podaje jako przykład anty-wzorca

def format_bands(bands):
    for band in bands:
        band['country'] = 'Canada'
        band['name'] = band['name'].replace('.', '')
        band['name'] = band['name'].title()

od

  • funkcja robi więcej niż jedną rzecz
  • nazwa nie jest opisowa
  • ma skutki uboczne

Jako zaproponowane rozwiązanie sugeruje anonimowe funkcje potokowe

pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
                      call(lambda x: x.replace('.', ''), 'name'),
                      call(str.title, 'name')])

Wydaje mi się jednak, że ma to tę wadę, że jest jeszcze mniej sprawdzalne; przynajmniej format_bands może mieć test jednostkowy, aby sprawdzić, czy robi to, co powinien, ale jak przetestować potok? Czy też pomysł, że funkcje anonimowe są tak oczywiste, że nie trzeba ich testować?

Moja aplikacja do tego celu polega na tym, aby mój pandaskod był bardziej funkcjonalny. Często mam jakiś potok wewnątrz funkcji „munging”

def munge_data(df)
     df['name'] = df['name'].str.lower()
     df = df.drop_duplicates()
     return df

Lub przepisywanie w stylu potoku:

def munge_data(df)
    munged = (df.assign(lambda x: x['name'].str.lower()
                .drop_duplicates())
    return munged

Wszelkie sugestie dotyczące najlepszych praktyk w takiej sytuacji?

Max Flander
źródło
4
Te indywidualne funkcje lambda są zbyt małe, aby je przetestować. Przetestuj wynik końcowy. Innymi słowy, anonimowe funkcje nie są testowane jednostkowo, więc nie zapisuj funkcji jako funkcji anonimowej, jeśli planujesz testować ją indywidualnie.
Robert Harvey

Odpowiedzi:

1

Myślę, że przegapiłeś chyba najważniejszą część poprawionego przykładu książki. Bardziej fundamentalna zmiana w kodzie dotyczy metody działającej na wszystkich wartościach listy na działanie na jednym elemencie.

Istnieją już funkcje takie jak iter(w tym przypadku o nazwie pipeline_foreach), które wykonują daną operację na wszystkich elementach listy. Nie trzeba było powielać tego za pomocą forpętli. Również użycie dobrze znanej operacji na liście wyjaśnia twoje zamiary. Wraz z maptobą przekształcasz wartości. Z iterwykonujesz efekt uboczny z każdym elementem. Dzięki forpętli jesteś ... cóż, tak naprawdę nie wiesz, dopóki tego nie przejrzysz.

Przykładowo poprawiony kod wciąż nie jest zbyt funkcjonalny, ponieważ (o ile wiem) mutuje wartości na liście bez zwracania ich, zapobiegając dalszemu potokowaniu lub układowi funkcji. Preferowana funkcjonalnie metoda mapstworzyłaby nową listę pasm ze zaktualizowanym countryi name. Następnie możesz przesłać dane wyjściowe do następnej funkcji lub skomponować mapz inną funkcją, która pobierała listę pasm. Z iter, to jak ślepy zaułek rurociągów.

Myślę, że kod wyniku końcowego ma małe funkcje, które są zbyt trywialne, aby zawracać sobie głowę testowaniem tutaj. W końcu nie powinieneś pisać testów jednostkowych przeciwko replacelub title. Być może teraz chcesz połączyć je razem we własne funkcje i testy jednostkowe, aby uzyskać pożądaną kombinację na jednym elemencie. Ja sam prawdopodobnie zmieniłbym się format_bandsna format_bandliczbę pojedynczą, porzuciłem pętlę for i zadzwoniłem pipeline_each(bands, format_band). Następnie możesz przetestować format_band, aby upewnić się, że niczego nie zapomniałeś.

W każdym razie przejdź do twojego kodu. Drugi przykład kodu wydaje się bardziej potokowy. Ale to nie zapewnia korzyści z programowania funkcjonalnego. W praktyce programowanie funkcjonalne oznacza zapewnienie zgodności funkcji z innymi funkcjami poprzez zdefiniowanie ich zgodności tylko w kategoriach ich wejść i wyjść. Jeśli wewnątrz funkcji są ukryte efekty uboczne, to pomimo tego, że jej wejście / wyjście jest zestawione z inną funkcją, nie możesz wiedzieć, czy są one kompatybilne do czasu uruchomienia. Jeśli jednak dwie funkcje nie wywołują skutków ubocznych i dopasowują dane wyjściowe do danych wejściowych, można je potokować lub komponować, nie martwiąc się o nieoczekiwane wyniki.

Kasey Speakman
źródło