Bau Kode 299 – Cara Memperbaiki Pengaturan Tes yang Berlebihan

Saat pengaturan tes Anda lebih besar dari tes yang sebenarnya

Tl; dr: pengaturan membengkak yang hanya digunakan sebagian membuat tes Anda lebih digabungkan dan lebih sulit untuk dipahami.

Masalah πŸ˜”

  • Kopel
  • Keterbacaan
  • Waktu eksekusi yang terbuang
  • Konteks pengaturan yang menyesatkan
  • Ketergantungan tes tersembunyi
  • Pemeliharaan yang lebih sulit
  • Suite uji rapuh
  • Ketergantungan yang membingungkan
  • Eksekusi lebih lambat
  • Konteks yang menyesatkan

Solusi πŸ˜ƒ

  1. Buat metode pengaturan yang terfokus
  2. Terapkan perlengkapan khusus tes
  3. Buat pengaturan minimal
  4. Menerapkan metode pabrik pengujian

Refactorings βš™οΈ

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

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

Konteks πŸ’¬

Saat Anda menulis tes, Anda dapat membuat metode pengaturan besar yang menginisialisasi berbagai objek.

Jika hanya satu tes menggunakan semua objek ini sementara tes lain hanya menggunakan subset kecil, Anda membuat overhead yang tidak perlu.

Masalah umum ini terjadi ketika Anda mengharapkan bahwa tes di masa depan mungkin memerlukan pengaturan yang luas, atau ketika Anda terus menambah pengaturan yang ada tanpa mengevaluasi apa yang benar -benar dibutuhkan.

Tes lebih sulit untuk dipahami karena mengandung konteks yang tidak relevan, dan lebih lambat untuk dieksekusi karena Anda menginisialisasi objek yang tidak digunakan.

Kode Sampel πŸ“–

Salah ❌

public class TVSeriesTest {
  private MovieSeries theEthernaut;
  private List characters;
  private List episodes;
  private User user;
  private UserPreferences preferences;
  private RatingSystem ratingSystem;
  private StreamingService streamingService;
  private List 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", "[emailΒ protected]", 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 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());
  }
}

Benar πŸ‘‰

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 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", "[emailΒ protected]", 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);
  }
}

Deteksi πŸ”

Anda dapat mendeteksi bau ini dengan membandingkan apa yang diatur dalam metode pengaturan terhadap apa yang digunakan dalam setiap tes.

Cari tes yang menggunakan kurang dari 50% objek yang diinisialisasi.

Alat cakupan kode dapat membantu mengidentifikasi objek pengaturan yang tidak digunakan dengan menunjukkan bagian mana dari pengaturan yang tidak dijalankan dengan tes tertentu.

Jika Anda menemukan diri Anda menulis kondisionalitas dalam pengaturan untuk membuat konteks yang berbeda, itu adalah tanda yang jelas Anda memerlukan pengaturan khusus tes.

Level πŸ”‹

Mengapa Bibe itu Penting πŸ—ΊοΈ

Setiap tes harus mencerminkan skenario dunia nyata tertentu.

Pengaturan membengkak mematahkan kejelasan ini, membuatnya sulit untuk melihat apa yang sedang diuji dan meningkatkan kemungkinan kesalahan.

Bijeksi yang rusak ini membuat tes lebih sulit dipahami karena Anda tidak dapat menentukan aspek pengaturan mana yang penting untuk tes dan mana yang hanya noise.

Ketika tes gagal, Anda akan menghabiskan lebih banyak waktu untuk menyelidiki dependensi yang mungkin tidak relevan dengan kegagalan.

Tes menjadi lebih rapuh karena perubahan pada objek yang tidak digunakan masih dapat merusak tes jika objek tersebut berpartisipasi dalam proses pengaturan.

AI Generasi πŸ€–

Generator kode AI sering membuat bau ini ketika mereka menghasilkan perlengkapan uji komprehensif yang mencoba untuk mencakup semua skenario yang mungkin.

Mereka memprioritaskan kelengkapan tentang fokus, menghasilkan metode pengaturan yang membengkak yang menginisialisasi lebih banyak objek daripada yang dibutuhkan untuk tes individu.

Deteksi AI πŸ₯ƒ

AI dapat mendeteksi bau ini dengan instruksi sederhana seperti “Optimalkan pengaturan tes saya hanya untuk memasukkan apa yang diperlukan untuk setiap tes.”

Alat AI modern dapat membandingkan kode pengaturan dengan penggunaan metode uji dan menyarankan refactorings yang ditargetkan, memisahkan pengaturan bersama dari pengaturan khusus tes.

Coba mereka! πŸ› 

Ingat: asisten ai membuat banyak kesalahan

Prompt Disarankan: Hancurkan tes dan pengaturan

Kesimpulan 🏁

Pengaturan tes yang kelebihan beban yang menginisialisasi objek yang hanya diperlukan oleh beberapa tes membuat suite tes Anda lebih sulit untuk dipahami dan dipelihara.

Saat Anda membuat pengaturan terfokus yang hanya berisi apa yang dibutuhkan setiap tes, Anda meningkatkan kejelasan, kecepatan, dan keandalan tes Anda.

Ingatlah bahwa tes bertujuan untuk mendokumentasikan perilaku melalui contoh dan mengganti komentar.

Konteks yang terlalu tidak relevan membuat contoh -contoh itu kurang mudah dibaca. Tes bersih menceritakan kisah yang jelas tanpa gangguan yang tidak perlu.

Hubungan πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘¨

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

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

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

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

Informasi lebih lanjut πŸ“•

Penafian πŸ“˜

Bau kode adalah pendapat saya.

Kredit πŸ™

Foto oleh Marcin Simonides di Unsplash


Jika Anda harus membuat banyak struktur sebelum tes, mungkin Anda menguji terlalu banyak lapisan

James Shore


Artikel ini adalah bagian dari seri CodeSmell.