Zacznijmy od przykładu.
Powiedzmy, że mam wywoływaną metodę, export
która zależy w dużym stopniu od schematu DB. Przez „mocno zależy” rozumiem, że dodanie nowej kolumny do określonej tabeli często (bardzo często) prowadzi do zmiany odpowiedniej export
metody (zwykle należy również dodać nowe pole do danych eksportu).
Programiści często zapominają o zmianie export
metody, ponieważ nie jest tak naprawdę jasne, że powinieneś nawet na to spojrzeć. Moim celem jest zmuszenie programisty do wyraźnego podjęcia decyzji, czy zapomniał spojrzeć na export
metodę, czy po prostu nie chce dodawać pola do danych eksportu. I szukam rozwiązania tego problemu.
Mam dwa pomysły, ale oba mają wady.
Inteligentne opakowanie „Przeczytaj wszystko”
Mogę utworzyć inteligentne opakowanie, które zapewni, że wszystkie dane zostaną odczytane.
Coś takiego:
def export():
checker = AllReadChecker.new(table_row)
name = checker.get('name')
surname = checker.get('surname')
checker.ignore('age') # explicitly ignore the "age" field
result = [name, surname] # or whatever
checker.check_now() # check all is read
return result
checker
Sprawdza więc, czy table_row
zawiera inne pola, które nie zostały odczytane. Ale wszystko to wygląda na ciężkie i (może) wpływa na wydajność.
„Sprawdź tę metodę” najczystsze
Mogę po prostu utworzyć unittest, który zapamiętuje ostatni schemat tabeli i zawiedzie przy każdej zmianie tabeli. W takim przypadku programista zobaczyłby coś w rodzaju „nie zapomnij sprawdzić export
metody”. Aby ukryć programator ostrzegający (lub nie chciałby - to problem), sprawdź export
i ręcznie (to kolejny problem) napraw test, dodając do niego nowe pola.
Mam kilka innych pomysłów, ale są one zbyt kłopotliwe do wdrożenia lub zbyt trudne do zrozumienia (i nie chcę, aby projekt stał się zagadką).
Powyższy problem jest tylko przykładem szerszej klasy problemów, które od czasu do czasu napotykam. Chcę powiązać niektóre fragmenty kodu i / lub infrastruktury, więc zmiana jednego z nich natychmiast ostrzega programistę, aby sprawdził inny. Zwykle masz kilka prostych narzędzi, takich jak wyodrębnianie wspólnej logiki lub pisanie rzetelnych, choć nieszablonowych, ale szukam tego narzędzia w bardziej skomplikowanych przypadkach: może niektóre wzorce projektowe są mi teraz znane.
źródło
export
na podstawie schematu?export
ma wszystko, czego naprawdę potrzebujesz?Odpowiedzi:
Jesteś na dobrej drodze ze swoim pomysłem na test jednostkowy, ale Twoja implementacja jest zła.
Jeśli
export
jest powiązany ze schematem i schemat się zmienił, istnieją dwa możliwe przypadki:Albo
export
nadal działa idealnie dobrze, ponieważ nie wpłynęło to na niewielką zmianę schematu,Lub się psuje.
W obu przypadkach celem kompilacji jest wyśledzenie tej możliwej regresji. Kilka testów - czy to testy integracyjne, testy systemowe, testy funkcjonalne lub coś innego - zapewniają, że twoja
export
procedura działa z bieżącym schematem, niezależnie od tego, czy zmieniła się od poprzedniego zatwierdzenia, czy nie. Jeśli te testy przejdą pomyślnie, świetnie. Jeśli zawiodą, jest to znak dla programisty, że mógł coś przeoczyć, i wyraźne wskazanie, gdzie szukać.Dlaczego Twoja implementacja jest nieprawidłowa? Z kilku powodów.
Nie ma to nic wspólnego z testami jednostkowymi ...
... a właściwie to nawet nie jest test.
Najgorsze jest to, że naprawienie „testu” wymaga, no cóż, faktycznej zmiany „testu”, czyli wykonania operacji całkowicie niezwiązanej z
export
.Zamiast tego, wykonując rzeczywiste testy
export
procedury, upewniasz się, że programista naprawi ten błądexport
.Mówiąc bardziej ogólnie, gdy napotkasz sytuację, w której zmiana w jednej klasie zawsze lub zwykle wymaga zmiany w zupełnie innej klasie, jest to dobry znak, że źle popełniłeś swój projekt i naruszasz zasadę pojedynczej odpowiedzialności.
Chociaż mówię konkretnie o zajęciach, dotyczy to mniej więcej innych podmiotów. Na przykład zmiana schematu bazy danych powinna albo zostać automatycznie odzwierciedlona w kodzie, na przykład za pomocą generatorów kodu używanych przez wiele ORM, albo przynajmniej powinna być łatwo zlokalizowana: jeśli dodam
Description
kolumnę doProduct
tabeli i nie używam ORM ani generatorów kodu, Spodziewam się przynajmniej jednej zmiany w obrębieData.Product
klasy DAL, bez konieczności przeszukiwania całej bazy kodu i znajdowania niektórych wystąpieńProduct
klasy w, powiedzmy, warstwie prezentacji.Jeśli nie możesz rozsądnie ograniczyć zmiany do jednej lokalizacji (albo dlatego, że po prostu nie działa, albo wymaga ogromnego rozwoju), możesz stworzyć ryzyko regresji . Kiedy zmieniam klasę
A
, a klasaB
gdzieś w bazie kodu przestaje działać, jest to regresja.Testowanie obniża ryzyko regresji i, co jest o wiele ważniejsze, pokazuje lokalizację regresji. Dlatego, gdy wiesz, że zmiany w jednej lokalizacji powodują problemy w zupełnie innej części bazy kodu, upewnij się, że masz wystarczającą liczbę testów, które wywołują alarmy, gdy tylko regresja pojawi się na tym poziomie.
We wszystkich przypadkach unikaj polegania w takich przypadkach tylko na komentarzach. Coś jak:
nigdy nie działa. Nie tylko programiści nie czytają go w większości przypadków, ale często kończy się albo usunięty, albo odsunięty od linii, której dotyczy, i staje się niemożliwy do zrozumienia.
źródło
If you change the following line...
co działa automatycznie i nie można go po prostu zignorować. Nigdy nie widziałem, żeby ktoś faktycznie używał takich pułapek, stąd wątpliwość.B
nie przestaje działać, może zaczyna działać nieprawidłowo. Ale nie mogę przewidzieć przyszłości i nie mogę napisać testów, które przewidują przyszłość, więc próbuję przypomnieć deweloperowi, aby napisał ten nowy test.Wydaje mi się, że twoje zmiany są nieokreślone. Załóżmy, że mieszkasz w miejscu, w którym nie ma kodów pocztowych, więc nie masz kolumny kodów pocztowych w tabeli adresów. Następnie wprowadzane są kody pocztowe lub zaczynasz kontaktować się z klientami mieszkającymi tam, gdzie są kody pocztowe i musisz dodać tę kolumnę do tabeli.
Jeśli element pracy mówi po prostu „dodaj kolumnę kodu pocztowego do tabeli adresów”, wówczas tak, eksport zostanie zerwany lub przynajmniej nie wyeksportuje kodów pocztowych. Ale co z ekranem wprowadzania, który służy do wprowadzania kodów pocztowych? Raport z listą wszystkich klientów i ich adresów? Istnieje wiele rzeczy, które należy zmienić po dodaniu tej kolumny. Zapamiętywanie tych rzeczy jest ważne - nie powinieneś liczyć na przypadkowe artefakty kodu, aby „uwięzić” programistów w zapamiętywaniu.
Gdy zostanie podjęta decyzja o dodaniu znaczącej kolumny (to znaczy nie tylko jakiegoś buforowanego całkowitego lub zdormalizowanego wyszukiwania lub innej wartości, która nie należy do eksportu, raportu lub ekranu wprowadzania), utworzone elementy pracy powinny zawierać WSZYSTKIE zmiany potrzebne - dodanie kolumny, aktualizacja skryptu wypełniania, aktualizacja testów, aktualizacja eksportu, raportów, ekranów wprowadzania danych i tak dalej. Nie wszystkie mogą być przypisane (lub odebrane) przez tę samą osobę, ale wszystkie muszą zostać wykonane.
Czasami programiści decydują się na samodzielne dodawanie kolumn w ramach wprowadzania większych zmian. Na przykład ktoś mógł napisać element roboczy, aby dodać coś do ekranu wprowadzania i raportu, bez zastanowienia się nad tym, jak jest to realizowane. Jeśli tak się często zdarza, musisz zdecydować, czy twój dodawca elementu pracy musi znać szczegóły implementacji (aby móc dodać wszystkie odpowiednie elementy pracy), czy też programiści muszą wiedzieć, że element roboczy- adder czasami pomija rzeczy. Jeśli jest to ta ostatnia, potrzebujesz kultury „nie zmieniaj schematu; przestań i pomyśl o tym, co jeszcze wpłynie”.
Gdyby było wielu programistów i zdarzyło się to więcej niż jeden raz, ustawiłbym alert meldowania dla kierownika zespołu lub innej wyższej osoby, aby otrzymywać powiadomienia o zmianach schematu. Osoba ta mogłaby następnie poszukać powiązanych elementów pracy, aby poradzić sobie z konsekwencjami zmiany schematu, a jeśli brakujących elementów pracy, mogła nie tylko je dodać, ale także edukować każdego, kto je pominął w planie.
źródło
Niemal zawsze podczas tworzenia eksportu tworzę również odpowiedni import. Ponieważ mam inne testy, które w pełni wypełniają eksportowaną strukturę danych, mogę następnie zbudować test jednostkowy w obie strony, który porównuje w pełni wypełniony oryginał z wyeksportowaną, a następnie zaimportowaną kopią. Jeśli są takie same, eksport / import jest zakończony; jeśli nie są takie same, test jednostkowy kończy się niepowodzeniem i wiem, że mechanizm eksportu wymaga aktualizacji.
źródło