Código Olor 299 - Cómo arreglar las configuraciones de prueba sobrecargadas

por Maximiliano Contieri7m2025/05/10
Read on Terminal Reader

Demasiado Largo; Para Leer

La configuración hinchada que solo se utiliza parcialmente hace que sus pruebas sean más acopladas y más difíciles de entender.
featured image - Código Olor 299 - Cómo arreglar las configuraciones de prueba sobrecargadas
Maximiliano Contieri HackerNoon profile picture

Cuando su configuración de prueba es mayor que la prueba real

TL;DR: La configuración inundada que solo se utiliza parcialmente hace que sus pruebas sean más acopladas y más difíciles de entender.

TL;DR: La configuración inundada que solo se utiliza parcialmente hace que sus pruebas sean más acopladas y más difíciles de entender.

Problemas

  • El Coupling
  • Legibilidad
  • Tiempo de ejecución perdido
  • Configuración del contexto engañoso
  • Test de dependencias ocultas
  • Mantenimiento más difícil
  • Suite de pruebas Brittle
  • Confusión de dependencias
  • Ejecución más lenta
  • Contexto engañoso

Soluciones

  1. Creación de métodos de configuración enfocados
  2. Aplica fixturas específicas de ensayo
  3. Crea configuraciones mínimas
  4. Implementación de métodos de prueba de fábrica

Recuperación ️

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

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

Contexto

Cuando escribe pruebas, puede crear un método de configuración grande que inicia varios objetos.


Si solo una prueba utiliza todos estos objetos, mientras que otras pruebas utilizan solo un pequeño subconjunto, se crea un overhead innecesario.


Este problema común ocurre cuando se espera que futuras pruebas necesiten una configuración extensa, o cuando se continúa añadiendo a una configuración existente sin evaluar lo que realmente se necesita.


Las pruebas son más difíciles de entender porque contienen un contexto irrelevante, y más lentas de ejecutar porque se inician objetos que no se utilizan.

Código de muestreo

equivocado 🙂

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

Derecho

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

Detección

  • [x] Semi-automático

Puede detectar este olor comparando lo que está configurado en los métodos de configuración con lo que se utiliza en cada prueba.


Busque pruebas que utilicen menos del 50% de los objetos inicializados.


Las herramientas de cobertura de código pueden ayudar a identificar los objetos de configuración no utilizados mostrando qué partes de la configuración no se ejecutan por ciertas pruebas.


Si te encuentras escribiendo condicionales en la configuración para crear diferentes contextos, es una señal clara de que necesitas una configuración específica de prueba en su lugar.

Tags ️

  • La prueba

Nivel

  • [x] El Intermedio

¿Por qué es importante la bijeción ️

Cada prueba debe reflejar un escenario específico del mundo real.


Las configuraciones hinchadas rompen esta claridad, lo que dificulta ver lo que se está probando y aumenta la posibilidad de errores.


Este rotoBijeciónhace que las pruebas sean más difíciles de entender porque no se puede determinar qué aspectos de la configuración son críticos para la prueba y cuáles son sólo ruido.


Cuando una prueba falla, pasarás más tiempo investigando dependencias que pueden no ser relevantes para el fracaso.


La prueba se vuelve más frágil ya que los cambios a los objetos no utilizados todavía pueden romper las pruebas si esos objetos participan en el proceso de configuración.

La generación

Los generadores de código de IA a menudo crean este olor cuando generan equipos de prueba completos que tratan de cubrir todos los escenarios posibles.


Priorizan la completitud sobre el enfoque, lo que resulta en métodos de configuración hinchados que inicializan más objetos de los necesarios para las pruebas individuales.

Detección

La IA puede detectar este olor con instrucciones simples como "Optimizar mi configuración de prueba solo para incluir lo que se necesita para cada prueba".


Las herramientas modernas de IA pueden comparar el código de configuración con el uso del método de prueba y sugerir refactorings dirigidos, separando la configuración compartida de la configuración específica de la prueba.

¡Pruébelo!

Recuerda: los asistentes de IA cometen muchos errores

Prompt sugerido: romper las pruebas y la configuración

Prompt sugerido: romper las pruebas y la configuración

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

Perplejidad

Perplejidad

piloto

piloto

Gemini

Gemini

profundidad

profundidad

El objetivo es

El objetivo es

Grúas

Grúas

Quien

Quien

Conclusión

Las configuraciones de prueba sobrecargadas que inicializan objetos necesarios solo para unos pocos tests hacen que su suite de pruebas sea más difícil de entender y mantener.

Cuando crea configuraciones focalizadas que contienen sólo lo que cada prueba necesita, mejora la claridad, la velocidad y la fiabilidad de sus pruebas.

Recuerde que las pruebas tienen como objetivo documentar el comportamiento a través de ejemplos ySustituye los comentarios.


Demasiado contexto irrelevante hace que esos ejemplos sean menos legibles.Pruebas limpias cuentan una historia clara sin distracciones innecesarias.

Relaciones

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

Más información

Disclaimer

Los olores son míosOpinión.

Créditos

Fotografía porMarcin SimónidesesUnosplash


Si tiene que crear mucha estructura antes de una prueba, tal vez esté probando a través de demasiadas capas

Si tiene que crear mucha estructura antes de una prueba, tal vez esté probando a través de demasiadas capas

por James Shore


Este artículo es parte de la serie CodeSmell.


Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks