Modele źródeł testowania jednostkowego

10

W moim niestandardowym rozszerzeniu mam kilka modeli, które służą tylko do wypełniania niektórych zaznaczeń i / lub wielokrotnych zaznaczeń w formie dodawania / edycji moich jednostek.
Są więc tym, co magento nazywa „modelami źródłowymi”.
Zaangażowane wartości są zawsze takie same, a metody zwracają to samo.
Jak powinienem je przetestować? A jeszcze lepiej, czy powinienem dla nich napisać testy jednostkowe?
Oto przykład.
Poniższa klasa służy do dodawania / edycji formularza dla pola o nazwie typei kolumny siatki tego samego pola.

<?php
namespace Sample\News\Model\Author\Source;

use Magento\Framework\Option\ArrayInterface;

class Type implements ArrayInterface
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;

    /**
     * Get options
     *
     * @return array
     */
    public function toOptionArray()
    {
        $_options = [
            [
                'value' => '',
                'label' => ''
            ],
            [
                'value' => self::COLLABORATOR,
                'label' => __('Collaborator')
            ],
            [
                'value' => self::EMPLOYEE,
                'label' => __('Employee')
            ],
        ];
        return $_options;
    }

    /**
     * get options as key value pair
     *
     * @return array
     */
    public function getOptions()
    {
        $_tmpOptions = $this->toOptionArray();
        $_options = [];
        foreach ($_tmpOptions as $option) {
            $_options[$option['value']] = $option['label'];
        }
        return $_options;
    }
}
Marius
źródło

Odpowiedzi:

15

To świetny wątek i bardzo lubię zarówno Odpowiedzi @KAndy, jak i @fschmengler.
Chciałbym dodać dodatkowe przemyślenia, które uważam za wartościowe, zadając pytanie takie jak „Czy powinienem przetestować X?” lub „Jak mam przetestować X?”.

Co mogłoby pójść źle?

  • Mógłbym zrobić głupią literówkę (zdarza się cały czas)
    To zwykle nie usprawiedliwia pisania testu.
  • Czy skopiuję potrzebny kod z rdzenia lub innego modułu, a następnie dostosuję go do moich potrzeb?
    Uważam to za bardzo niebezpieczną rzecz, która często pozostawia subtelne błędy. W takim przypadku wolę napisać test, jeśli nie jest zbyt drogi. Oparcie konfiguracji modeli źródłowych w rzeczywistości uczyniłoby je bardziej ryzykownymi IMO.
  • Czy może wystąpić konflikt z innym modułem?
    Dotyczy to prawie tylko kodu konfiguracji. W takim przypadku lubię mieć test integracyjny, który powie mi, kiedy to nastąpi.
  • Czy Magento może zmienić interfejs API w przyszłej wersji?
    W tym przypadku bardzo mało prawdopodobne, ponieważ Twój kod zależy tylko od interfejsu. Ale im bardziej konkretne są zaangażowane klasy lub jeśli mój kod rozszerza klasę podstawową, staje się to bardziej potencjalnym ryzykiem.
  • Nowa wersja PHP może uszkodzić mój kod. A może chcę w przyszłości wspierać PHP 5.6.
    Ponownie, bardzo mało prawdopodobne, ale w niektórych przypadkach chcę, aby test ostrzegł mnie, czy powinienem zmienić kod w przyszłości, aby użyć niezgodnej składni.

Ile kosztuje testowanie kodu?

Ma to dwa aspekty:

  • Ile wysiłku i czasu zajmuje napisanie testu
  • Ile wysiłku i czasu zajmuje przetestowanie fragmentu kodu, który zamierzam napisać ręcznie.

Podczas opracowywania fragmentu kodu mam tendencję do uruchamiania kodu, który piszę dość często, dopóki nie uznam go za gotowy. Jest to oczywiście znacznie łatwiejsze w przypadku testu jednostkowego.

W twoim przypadku napisanie testu jest naprawdę tanie. Nie zajmuje dużo czasu ani wysiłku. @KAndy ma rację, że cały kod musi być utrzymany, ale z drugiej strony nie wszystkie testy muszą być zachowane.
Może to być przykład, w którym napisałbym test jednostkowy, aby sprawdzić, czy nie popełniam głupiego błędu, a następnie usunąć go po zakończeniu zajęć. Jeśli test nie zapewnia długoterminowej wartości, myślę, że usunięcie go ma sens.

To pytanie jest również ważne z punktu widzenia wyboru rodzaju testu do napisania: na przykład jednostka lub integracja.

Jak cenny jest fragment kodu, który piszę?

Jeśli fragment kodu, który piszę, jest niezbędny do świadczenia usługi przez moduł, testuję go, niezależnie od tego, jak trywialny jest.
Jeśli jest to tylko mała metoda użyteczności, na przykład skoncentrowana na interfejsie użytkownika, a nie część logiki biznesowej, to może nie.

Czy kod będzie musiał się zmienić?

Z czasem przyzwyczaiłem się do pokrycia testowego, że zmiana odkrytego kodu jest bardzo niepewna. Obejmuje to tak proste rzeczy, jak dodanie opcji do modelu źródłowego, ale także rzeczy takie jak przeniesienie klasy do innego folderu / przestrzeni nazw lub zmiana nazwy metody.
Przygotowanie testów takich zmian jest nieocenione.

Czy potrzebuje dokumentacji?

Jak trudno jest użyć kodu? W twoim przykładzie jest to trywialne, ale w bardziej skomplikowanych przypadkach, test jest świetny do celów dokumentacyjnych dla innych programistów (lub mnie za kilka miesięcy).

Eksploracja i uczenie się

Jeśli pracuję nad jakimś kodem i nie jestem pewien, jak go przetestować, uważam, że napisanie testu jest bardzo cenne. Proces ten prawie zawsze pozwala mi lepiej zrozumieć, z czym mam do czynienia.
Jest to szczególnie ważne dla programistów, którzy nadal uważają się za uczenie się testowania.
Jest to również przykład, w którym sensowne może być późniejsze usunięcie testu, ponieważ jego główną wartością było uczenie się.

Dyscyplina i stres

Trzymanie się pętli refaktora czerwono-zielonego pomaga mi iść szybko. Jest to szczególnie prawdziwe pod presją. Więc nawet jeśli jakiś fragment kodu nie jest naprawdę warty przetestowania, nadal mogę stosować TDD, szczególnie jeśli kod jest trywialny do przetestowania.
To utrzymuje mnie w przepływie i czujności.

Co i jak testować?

Weź również pod uwagę, że możesz napisać test w bardzo różnym stopniu szczegółowości.

  • Testowanie dokładnej wartości zwracanej.
    To będzie bardzo sztywny test, który będzie musiał być dostosowany do każdej zmiany. Czy chcesz przerwać test, na przykład, jeśli zmieni się kolejność elementów w tablicy zwrotnej?
  • Testowanie struktury wartości zwracanej.
    W przypadku modelu źródłowego może to sprawdzać każdą pod-tablicę jako dwa rekordy, jeden z kluczem labela drugi z valuekluczem.
  • Sprawdzanie implementacji klasy ArrayInterface.
  • Testowanie klasy zapewnia, getOptions()mimo że ta metoda nie jest częścią zaimplementowanego interfejsu.

Dla każdej możliwej rzeczy, którą można przetestować, rozważ wartość, łatwość konserwacji i koszt.

Podsumowanie

Podsumowując: nie ma jednej odpowiedzi na pytanie, czy jakiś fragment kodu powinien zostać przetestowany, czy nie. Odpowiedź będzie inna dla każdego programisty w zależności od okoliczności.

Vinai
źródło
2
Świetna odpowiedź! Chcę podkreślić część „usuń testy, gdy nie zapewniają już wartości” - czasami stare testy, które pomogły podczas początkowego rozwoju, stają się przeszkodą na dłuższą metę.
Fabian Schmengler
1
właśnie odpowiedziałeś na 2 inne pytania, o które wątpiłem. dziękuję
Marius
6

Moim zdaniem nie ma ogólnej odpowiedzi na „napisz testy jednostkowe dla modeli źródłowych, tak lub nie”

Napisałem testy jednostkowe dla modeli źródłowych, ale były to dynamiczne modele źródłowe, które pobierały dane zewnętrzne i tam ma to sens.

W przypadku modeli źródłowych, które są niczym więcej niż uwielbionymi tablicami (jak w twoim przykładzie), nie zawracałbym sobie głowy pisaniem testów jednostkowych. Ale w jakiś sposób musisz upewnić się, że nie popełniłeś błędu. Istnieje kilka opcji:

  1. test jednostkowy
  2. test integracyjny
  3. ręcznie sprawdź konfigurację

Czy śledzisz TDD? Następnie wybierz pomiędzy (1) i (2) lub obu. W przeciwnym razie wybierz pomiędzy (2) a (3).


Jeśli używasz modeli źródłowych dla opcji konfiguracji systemu, test integracji (jeden test dla wszystkich opcji konfiguracji) może wyświetlić sekcję konfiguracji i sprawdzić, czy <select>elementy są obecne i zawierają oczekiwane wartości. Pamiętaj, że nie chodzi tu o testowanie jednej konkretnej klasy, ale sprawdzenie, czy wszystko jest ze sobą poprawnie powiązane.


I jak powiedział @KAndy, idealnie nie potrzebujesz takiej płyty kotłowej, wystarczy rozszerzyć klasę bazową, która jest już testowana jednostkowo i zastąpić właściwość lub zdefiniować wartości w konfiguracji zewnętrznej.

W tym scenariuszu testy jednostkowe dla konkretnych wdrożeń nie zapewniają żadnej dodatkowej wartości. Jeśli masz wiele z tych klas, dobrym pomysłem może być samodzielne napisanie klasy podstawowej ArraySource, o ile Magento jej nie zapewnia.


Przy takiej klasie bazowej model źródłowy może wyglądać następująco:

class Type extends ArraySource
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;
    protected $elements = [
        self::COLLABORATOR => 'Collaborator',
        self::EMPLOYEE     => 'Employee',
    ];
    protected $withEmpty = true;
    protected $translate = true;
}
Fabian Schmengler
źródło
Dziękuję za potwierdzenie. Spróbuję przekształcić moje uwielbione tablice w konfigurowalną listę opcji, ale czy to samo dotyczy modeli, które będą miały dokładnie N opcji? Jak model źródłowy „status”.
Marius
Nie rozumiem, w jaki sposób model źródłowy „status”
różniłby się
ponieważ mógłbym użyć wartości 0 i 1 (jako stałych klas) w interfejsie, aby filtrować na przykład włączone jednostki. Mam na myśli, że w tym przypadku wartości opcji mają za sobą logikę. to nie są tylko key=>valuepary.
Marius
Ale ta logika nie jest częścią modelu źródłowego, prawda? Oznacza to, że nie ma to znaczenia. Nadal będziesz mieć stałe do użycia w innych miejscach. Dodałem przykład, w jaki sposób używałbym takiej implantacji.
Fabian Schmengler
5

Jak powinienem je przetestować?

Myślę, że nie powinieneś.

Dodaj kod do systemu, aby zwiększyć koszty wsparcia i konserwacji, ale proces testowania powinien być LEAN .

Więcej na temat tego kodu nie powinno istnieć. Uważam, że w Magento powinien być jeden ogólny deklaratywny sposób definiowania zestawów opcji do użycia w różnych miejscach. A twoja niechęć do pisania testu na tych zajęciach pokazuje mi zapach złego kodu

KAndy
źródło
1
Dzięki. Mówisz więc, że te opcje nie powinny być zakodowane na stałe, ale powinny pochodzić z pliku konfiguracyjnego (na przykład di.xml). Mogę to zrobić. Ale czy to samo dotyczy modeli źródeł statusu? Mam na myśli, jeśli mam tę samą klasę, co powyżej, ale tylko z włączonym i wyłączonym statusem (1,0), czy powinienem używać tej samej metody konfiguracji? Jeśli tak, chcę powiedzieć, że dla mnie wygląda to na nadmiar inżynierii.
Marius