Code Smell 299 - Kako popraviti preopterećene postavke testova

by Maximiliano Contieri7m2025/05/10
Read on Terminal Reader

Predugo; Citati

Prekomjerno podešavanje koje se koristi samo djelomično čini testove povezanim i teškim za razumijevanje.
featured image - Code Smell 299 - Kako popraviti preopterećene postavke testova
Maximiliano Contieri HackerNoon profile picture

Kada je postavljanje testa veće od stvarnog testa

TL;DR: Prekomjerno podešavanje koje se koristi samo djelomično čini vaše testove povezanim i teže razumljivim.

TL;DR: Prekomjerno podešavanje koje se koristi samo djelomično čini vaše testove povezanim i teže razumljivim.

Problemi

  • Spajanje
  • čitljivost
  • Potrošeno vreme za izvršenje
  • Pogrešno postavljanje konteksta
  • Skriven test ovisnosti
  • Teže održavanje
  • Brittle test suite uređaja
  • Zavisnost od zavisnosti
  • Sporije izvršenje
  • Pogrešan kontekst

Rešenja

  1. Stvaranje fokusiranih setup metoda
  2. Upotreba test-specifičnih fixtura
  3. Stvaranje minimalnih postavki
  4. Provedba test fabrike metode

Preporuke ️

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

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

Kontekst

Kada pišete testove, možda ćete stvoriti veliku metodu podešavanja koja inicijalizira različite objekte.


Ako samo jedan test koristi sve ove objekte, dok drugi testovi koriste samo mali podskup, kreirate nepotrebne nadređene.


Ovaj uobičajeni problem se javlja kada očekujete da budući testovi mogu zahtijevati opsežno podešavanje ili kada nastavljate dodavanje postojećeg podešavanja bez procjene onoga što je zaista potrebno.


Testovi su teže razumjeti jer sadrže nevažeći kontekst, a sporije se izvode jer inicijalizujete objekte koji se ne koriste.

Sample kod

Pogrešno 🙂

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());
  }
}

U pravu 🙂

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);
  }
}

Detekcija

  • [x] Poluautomatski

Ovaj miris možete otkriti usporedbom onoga što je postavljeno u metodama podešavanja protiv onoga što se koristi u svakom testu.


Potražite testove koji koriste manje od 50% inicijaliziranih objekata.


Alatke za pokrivanje koda mogu pomoći u identifikaciji neiskorištenih objekata za podešavanje tako što će pokazati koji dijelovi podešavanja nisu izvršeni određenim testovima.


Ako se nađete pisanje uslovljavanja u podešavanju za stvaranje različitih konteksta, to je jasan znak da je potrebna test specifična podešavanja umesto toga.

U toku dana ️

  • Testiranje

Nivo

  • [x] Posrednik

Zašto je bijecija važna ️

Svaki test bi trebao odražavati određeni scenarij u stvarnom svetu.


Napuhana podešavanja prekidaju ovu jasnoću, čineći teško vidjeti što se testira i povećavajući šansu za greške.


Ova slomljenaBijecijačini testove teže razumjeti jer ne možete odrediti koji aspekti podešavanja su kritični za test, a koji su samo buku.


Kada test ne uspije, provesti ćete više vremena istražujući ovisnosti koje možda nisu relevantne za neuspeh.


Test postaje krhkiji jer promene neiskorišćenih objekata i dalje mogu prekinuti testove ako ti objekti učestvuju u procesu podešavanja.

Ova generacija

AI generatori koda često stvaraju taj miris kada stvaraju sveobuhvatne testove koji pokušavaju pokriti sve moguće scenarije.


Oni daju prioritet potpunosti u odnosu na fokus, što rezultira natečenim metodama podešavanja koje inicijaliziraju više objekata nego što je potrebno za pojedinačne testove.

Detekcija

AI može otkriti ovaj miris pomoću jednostavnih uputa kao što su "Optimizirajte moju konfiguraciju testa da biste uključili samo ono što je potrebno za svaki test."


Moderne AI alate mogu usporediti konfiguracijski kod s upotrebom test metode i predložiti ciljanu refaktoring, razdvajajući zajedničku konfiguraciju od konfiguracije specifične za test.

Isprobajte ih!

Zapamtite: AI asistenti čine mnogo grešaka

Predložen prompt: Razbijte testove i podešavanje

Predložen prompt: Razbijte testove i podešavanje

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

Čačak

Čačak

Klod

Klod

zbunjenost

zbunjenost

Piloti

Piloti

Blizanci

Blizanci

duboko

duboko

Cilj AI

Cilj AI

Grožđe

Grožđe

Knjiga

Knjiga

Zaključak

Preopterećene postavke za testiranje koje inicijaliziraju objekte potrebne samo za nekoliko testova otežavaju razumevanje i održavanje testove.

Kada kreirate fokusirane podešavanja koja sadrže samo ono što svaki test treba, poboljšate jasnoću, brzinu i pouzdanost vaših testova.

Zapamtite da testovi imaju za cilj dokumentiranje ponašanja pomoću primjera iZamjena komentara.


Previše irelevantnog konteksta čini te primere manje čitljivim. Čisti testovi govore jasnu priču bez nepotrebnih distraccija.

Odnosi

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

Više informacija

Disclaimer

Mirisi su mojiMišljenje.

Krediti

Fotografije odMarko SimonićjeUslovi


Ako morate stvoriti mnogo strukture prije testa, možda testirate kroz previše slojeva

Ako morate stvoriti mnogo strukture prije testa, možda testirate kroz previše slojeva

Džejms Šori


Ovaj članak je deo CodeSmell serije.


L O A D I N G
. . . comments & more!

About Author

Maximiliano Contieri HackerNoon profile picture
Maximiliano Contieri@mcsee
I’m a sr software engineer specialized in Clean Code, Design and TDD Book "Clean Code Cookbook" 500+ articles written

HANG TAGS

OVAJ ČLANAK JE PREDSTAVLJEN U...

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks