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
- Stvaranje fokusiranih setup metoda
- Upotreba test-specifičnih fixtura
- Stvaranje minimalnih postavki
- 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 |
---|---|
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.