Kod Smell 299 - Jak naprawić przeciążone ustawienia testowe

przez Maximiliano Contieri7m2025/05/10
Read on Terminal Reader

Za długo; Czytać

Przytłaczająca konfiguracja, która jest używana tylko częściowo, sprawia, że testy są bardziej połączone i trudniejsze do zrozumienia.
featured image - Kod Smell 299 - Jak naprawić przeciążone ustawienia testowe
Maximiliano Contieri HackerNoon profile picture

Gdy ustawienie testu jest większe niż rzeczywisty test

TL;DR: Zawieszona konfiguracja, która jest używana tylko częściowo, sprawia, że testy są bardziej połączone i trudniejsze do zrozumienia.

TL;DR: Zawieszona konfiguracja, która jest używana tylko częściowo, sprawia, że testy są bardziej połączone i trudniejsze do zrozumienia.

Problemy

  • parowanie
  • Czytelność
  • Zmarnowany czas egzekucji
  • Oszukiwanie kontekstu
  • Ukryte uzależnienia testowe
  • Trudniejsza konserwacja
  • Brittle testowe apartamenty
  • Zmieszanie zależności
  • Powolna egzekucja
  • Błędny kontekst

Rozwiązania

  1. Tworzenie ukierunkowanych metod instalacji
  2. Zastosuj test-specyficzne fixtures
  3. Tworzenie minimalnych ustawień
  4. Wdrażanie metod testowych fabrycznych

Rewitalizacja ️

https://hackernoon.com/improving-the-code-one-line-at-a-time

https://maximilianocontieri.com/refactoring-011-replace-comments-with-tests

Kontekst

Podczas pisania testów można utworzyć dużą metodę konfiguracji, która inicjuje różne obiekty.


Jeśli tylko jeden test używa wszystkich tych obiektów, podczas gdy inne testy używają tylko niewielkiego podzbioru, tworzysz niepotrzebny nadmiar.


Ten powszechny problem występuje, gdy spodziewasz się, że przyszłe testy mogą wymagać rozległej konfiguracji lub gdy kontynuujesz dodawanie do istniejącej konfiguracji bez oceny tego, co jest naprawdę potrzebne.


Testy są trudniejsze do zrozumienia, ponieważ zawierają nieistotne konteksty, a wolniejsze do wykonania, ponieważ inicjalizujesz obiekty, które nie są używane.

Przykładowy kod

Błędne 🙂

public class TVSeriesTest {
  private MovieSeries theEthernaut;
  private List<Character> characters;
  private List<Episode> episodes;
  private User user;
  private UserPreferences preferences;
  private RatingSystem ratingSystem;
  private StreamingService streamingService;
  private List<Review> reviews;
  
  @BeforeEach
  public void setUp() {
    // Create a complex movie series with many characters
    characters = new ArrayList<>();
    characters.add(new Character("Juan Salvo", "Richard Darin"));
    characters.add(new Character("Helen", "Carla Peterson")); 
    characters.add(new Character("Favalli", "Cesar Troncoso")); 
    
    // Create episodes
    episodes = new ArrayList<>();
    episodes.add(
      new Episode("The Snow", 2025, 121));
    episodes.add(
      new Episode("The Hands Strikes Back", 2027, 124)); 
    
    // Create user with preferences
    preferences = new UserPreferences();
    preferences.setPreferredGenre("Science Fiction");
    preferences.setPreferredLanguage("English");
    preferences.setSubtitlesEnabled(true);
    user = new User("JohnDoe", "john@example.com", preferences);
    
    // Create rating system with reviews
    ratingSystem = new RatingSystem(10);
    reviews = new ArrayList<>();
    reviews.add(
      new Review(user, "The Snow", 9, "Classic!"));
    reviews.add(
      new Review(user, "The Hands Strikes Back", 10, "Best one!"));
    ratingSystem.addReviews(reviews);
    
    // Create streaming service
    streamingService = new StreamingService("Netflix");
    streamingService.addMovieSeries("The Eternaut");
    
    // Finally create the movie series with all components
    theEthernaut = 
      new TVSeries("The Ethernaut", characters, episodes);
    theEthernaut.setRatingSystem(ratingSystem);
    theEthernaut.setAvailableOn(streamingService);
    
    // This method is too long. That is another smell
  }
  
  @Test
  public void testTVSeriesRecommendation() {
    // This test uses almost everything from the setup
    RecommendationEngine engine = new RecommendationEngine();
    List<Episode> recommended =
      engine.recommendations(user, theEternaut);
    
    assertEquals(2, recommended.size());
    assertEquals("The Hands Strikes Back",
      recommended.get(0).title());
    // You are testing the recomendation Engine
    // This is not this object's responsibility
  }
  
  @Test
  public void testEpisodeCount() {
    // This test only needs the episodes count
    assertEquals(2, theEthernaut.episodes().size());
  }
  
  @Test
  public void testCharacterLookup() {
    // This test only needs the characters
    // And not the rest of the setup
    Character juan = theEternaut.findCharacterByName("Juan Salvo");
    assertNotNull(juan);
    assertEquals("Juan Salvo", juan.actor());
  }
}

Właściwie 🙂

public class TVSeriesTest {
  // No shared setup
  
  @Test
  public void testRecommendation() {
    // Create only what's needed for this specific test
    // And move this test with the behavior
    TVSeries theEternaut = createTheEternautSeries();
    User homer = createUserWithPreferences();
    addReviewsForUser(theEternaut, homer);
    
    RecommendationEngine engine = new RecommendationEngine();
    List<Episode> recommended =
      engine.recommendations(homer, theEternaut);
    
    assertEquals(2, recommended.size());
    assertEquals("The Hands Strikes Back", 
      recommended.get(0).title());
  }
  
  @Test
  public void testEpisodeCount() {
    // Only create what's needed - just the episodes
    TVSeries theEternaut = new TVSeries("The Ethernaut");
    theEternaut.addEpisode(
      new Episode("The Snow", 2025, 121));
    theEternaut.addEpisode(
      new Episode("The Hands Strikes Back", 2027, 124)); 
    
    assertEquals(2, theEternaut.episodes().size());
  }
  
  @Test
  public void testCharacterLookup() {
    // Only create what's needed - just the characters
    TVSeries theEternaut = new TVSeries("The Eternaut");
    theEternaut.addCharacter(
      new Character("Juan Salvo", "Richard Darin"));
    theEternaut.addCharacter(
      new Character("Helen", "Carla Peterson")); 
    
    Character juan = theEternaut.findCharacterByName("Juan Salvo");
    assertNotNull(juan);
    assertEquals("Richard Darin", juan.actor());
  }
  
  // Helper methods for specific test setup needs
  private TVSeries createTheEternautTVSeries() {
    TVSeries series = new TVSeries("The Eternaut");
    series.addEpisode(
      new Episode("The Snow", 2025, 121));
    series.addEpisode(
      new Episode("The Hands Strikes Back", 2027, 124)); 
    return series;
  }
  
  private User createUserWithPreferences() {
    UserPreferences preferences = new UserPreferences();
    preferences.setPreferredGenre("Science Fiction");
    preferences.setPreferredLanguage("English");
    return new User("JohnDoe", "john@example.com", preferences);
  }
  
  private void addReviewsForUser(TVSeries series, User user) {
    RatingSystem ratingSystem = new RatingSystem(10);
    ratingSystem.addReview(
      new Review(user, "The Snow", 9, "Classic!"));
    ratingSystem.addReview(
      new Review(user, "The Hands Strikes Back", 10, "Best one!"));
    series.setRatingSystem(ratingSystem);
  }
}

Wykrywanie

  • [x] Półautomatyczny

Ten zapach można wykryć, porównując to, co jest ustawione w metodach konfiguracji z tym, co jest używane w każdym teście.


Szukaj testów, które wykorzystują mniej niż 50% zainicjowanych obiektów.


Narzędzia pokrycia kodu mogą pomóc w identyfikacji nieużywanych obiektów instalacyjnych, pokazując, które części instalacji nie są wykonywane przez niektóre testy.


Jeśli znajdujesz się pisanie warunków w konfiguracji, aby utworzyć różne konteksty, jest to wyraźny znak, że potrzebujesz konfiguracji specyficznej dla testów.

Tagi ️

  • Testowanie

Poziom

  • [x] Pośrednik

Dlaczego biżuteria jest ważna ️

Każdy test powinien odzwierciedlać konkretny scenariusz rzeczywisty.


Zapylone ustawienia łamią tę jasność, utrudniając widzenie tego, co jest testowane i zwiększając prawdopodobieństwo błędów.


Ten złamanyBijecjasprawia, że testy są trudniejsze do zrozumienia, ponieważ nie można określić, które aspekty konfiguracji są krytyczne dla testu, a które są tylko hałasem.


Gdy test zawiedzie, będziesz spędzać więcej czasu badając uzależnienia, które mogą nie być istotne dla niepowodzenia.


Badanie staje się bardziej kruche, ponieważ zmiany nieużywanych obiektów mogą nadal przerywać testy, jeśli te obiekty biorą udział w procesie konfiguracji.

I pokolenie

Generatory kodów AI często tworzą ten zapach, gdy generują kompleksowe zestawy testowe, które próbują pokryć wszystkie możliwe scenariusze.


Priorytetem jest kompletność nad koncentracją, co powoduje opuchnięte metody konfiguracji, które inicjują więcej obiektów niż jest to konieczne dla poszczególnych testów.

Jak wykryć

Sztuczna inteligencja może wykryć ten zapach za pomocą prostych instrukcji, takich jak "Optimalizuj moje ustawienia testowe, aby uwzględnić tylko to, co jest potrzebne dla każdego testu".


Nowoczesne narzędzia sztucznej inteligencji mogą porównywać kod konfiguracyjny z użyciem metody testowej i sugerować ukierunkowane refaktory, oddzielając udostępnioną konfigurację od konfiguracji specyficznej dla testów.

Spróbujcie ich! 🙂

Pamiętaj: asystenci AI popełniają wiele błędów

Sugested Prompt: Przełamać testy i ustawienia

Sugested Prompt: Przełamać testy i ustawienia

Without Proper Instructions

With Specific Instructions

ChatGPT

ChatGPT

Claude

Claude

Perplexity

Perplexity

Copilot

Copilot

Gemini

Gemini

DeepSeek

DeepSeek

Meta AI

Meta AI

Grok

Grok

Qwen

Qwen

ChatGPT

ChatGPT

Claude

Claude

Zdezorientowanie

Zdezorientowanie

Dziecięcy pilot

Dziecięcy pilot

Bliźnięta

Bliźnięta

głębokie

głębokie

Celem AI

Celem AI

Groch

Groch

Kwiat

Kwiat

Podsumowanie

Przeładowane ustawienia testowe, które inicjują obiekty wymagane tylko przez kilka testów, utrudniają zrozumienie i utrzymanie pakietu testowego.

Podczas tworzenia ukierunkowanych ustawień, które zawierają tylko to, czego potrzebuje każdy test, poprawia się przejrzystość, szybkość i niezawodność testów.

Pamiętaj, że testy mają na celu udokumentowanie zachowania za pomocą przykładów iZastąp komentarze.


Zbyt dużo nieistotnego kontekstu czyni te przykłady mniej czytelnymi.Czyste testy opowiadają jasną historię bez zbędnych rozpraszania.

Relacje

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxv

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xi-sit35t1

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxiii

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xli

Więcej informacji

Disclaimer

Kodowe zapachy są mojeOpinia.

Kredyty

Zdjęcie przezMarcin SimonidesJestUnsplash


Jeśli musisz stworzyć dużo struktury przed testem, być może testujesz zbyt wiele warstw

Jeśli musisz stworzyć dużo struktury przed testem, być może testujesz zbyt wiele warstw

- James Shore


Ten artykuł jest częścią serii CodeSmell.


Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks