Cando a configuración da proba é maior que a proba real
TL;DR: unha configuración inundada que só se usa parcialmente fai que os teus test sexan máis combinados e máis difíciles de entender.
TL;DR: unha configuración inundada que só se usa parcialmente fai que os teus test sexan máis combinados e máis difíciles de entender.
Problemas
- Coupling
- lexibilidade
- Perda de tempo de execución
- Configuración dun contexto erróneo
- Test de dependencia oculta
- Manutención máis difícil
- Suite de probas Brittle
- Confusión dependencia
- Execución máis lenta
- Contexto erróneo
Solucións
- Creación de métodos de configuración enfocados
- Aplica fixacións de proba específicas
- Crear configuracións mínimas
- Implementación de métodos de proba 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
O contexto
Cando escribe probas, pode crear un gran método de configuración que inicia varios obxectos.
Se só unha proba usa todos estes obxectos, mentres que outras probas usan só un pequeno subconxunto, crea un superconxunto innecesario.
Este problema común ocorre cando espera que futuras probas poidan necesitar unha configuración extensa, ou cando continúa engadindo a unha configuración existente sen avaliar o que realmente é necesario.
As probas son máis difíciles de entender porque conteñen contexto irrelevante e máis lentas de executar porque se inician obxectos que non se usan.
Modelo de código
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());
}
}
Xusto 🙂
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
Pode detectar este cheiro comparando o que está configurado nos métodos de configuración contra o que se usa en cada proba.
Buscar probas que utilicen menos do 50% dos obxectos inicializados.
As ferramentas de cobertura de código poden axudar a identificar obxectos de configuración non utilizados mostrando que partes da configuración non se executan por certas probas.
Se vostede se atopa escribindo condicionais na configuración para crear diferentes contextos, é un sinal claro de que necesita unha configuración específica de proba no seu lugar.
Xogos ️
- Probas
Nivel
- [x] Intermediarios
Por que é tan importante a bijeción ️
Cada proba debe reflectir un escenario específico do mundo real.
As configuracións inchadas rompen esta claridade, o que dificulta ver o que está a ser probado e aumenta a probabilidade de erros.
Esta desfeitaBixeciónFacer que as probas sexan máis difíciles de comprender porque non se pode determinar que aspectos da configuración son críticos para a proba e que son só ruídos.
Cando unha proba fracase, pasarás máis tempo investigando dependencias que poden non ser relevantes para o fracaso.
A proba faise máis fráxil xa que os cambios a obxectos non utilizados aínda poden romper as probas se eses obxectos participan no proceso de configuración.
A miña xeración
Os xeradores de código de IA a miúdo crean este cheiro cando xeran equipos de proba abrangentes que intentan cubrir todos os escenarios posibles.
Priorizan a completude sobre o foco, resultando en métodos de configuración inchados que inician máis obxectos do que é necesario para probas individuais.
A súa detección
A IA pode detectar este cheiro con instrucións sinxelas como "Optimizar a miña configuración de proba só para incluír o que se necesita para cada proba".
As modernas ferramentas de IA poden comparar o código de configuración co uso do método de proba e suxerir refactorings dirixidos, separando a configuración compartida da configuración específica de probas.
Proba con eles!
Lembra: os asistentes de IA cometen moitos erros
Prompt suxerido: romper as probas e a configuración
Prompt suxerido: romper as probas e a configuración
Without Proper Instructions |
With Specific Instructions |
---|---|
Conclusión
As configuracións de proba sobrecargadas que inicializan obxectos necesarios só por uns poucos ensaios fan que a súa suite de ensaios sexa máis difícil de entender e manter.
Cando crea configuracións focalizadas que conteñan só o que precisa cada proba, mellora a claridade, a velocidade e a fiabilidade das súas probas.
Lembre que as probas teñen como obxectivo documentar o comportamento a través de exemplos eSubstituír comentarios.
Demasiado contexto irrelevante fai que eses exemplos sexan menos lexibles.Probas limpas contan unha historia clara sen distraccións innecesarias.
Relacións
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áis información
Disclaimer
Os cheiros son meusOpinión.
Créditos
Imaxe porMartiño SimónqueUnsplash
Se ten que crear unha gran cantidade de estrutura antes dunha proba, quizais está probando a través de demasiadas capas
Se ten que crear unha gran cantidade de estrutura antes dunha proba, quizais está probando a través de demasiadas capas
Xesús Shore
Este artigo forma parte da serie CodeSmell.