Khi thiết lập thử nghiệm của bạn lớn hơn so với thử nghiệm thực tế
TL;DR: Cài đặt tràn ngập chỉ được sử dụng một phần làm cho các bài kiểm tra của bạn kết hợp hơn và khó hiểu hơn.
TL;DR: Cài đặt tràn ngập chỉ được sử dụng một phần làm cho các bài kiểm tra của bạn kết hợp hơn và khó hiểu hơn.
Vấn đề
- Coupling
- Đọc
- lãng phí thời gian thi hành
- Lời bài hát: Misleading Context
- Test phụ thuộc ẩn
- Bảo trì khó khăn hơn
- Phòng thử nghiệm Brittle
- Sự phụ thuộc nhầm lẫn
- thi hành chậm hơn
- ngữ cảnh lừa đảo
Giải pháp
- Tạo phương pháp Setup Focused
- Sử dụng test-specific fixtures
- Tạo setup tối thiểu
- Thực hiện các phương pháp thử nghiệm nhà máy
Đánh giá ️
https://hackernoon.com/improving-the-code-one-line-at-a-time
https://maximilianocontieri.com/refactoring-011-replace-comments-with-tests
bối cảnh
Khi bạn viết các bài kiểm tra, bạn có thể tạo một phương pháp thiết lập lớn bắt đầu các đối tượng khác nhau.
Nếu chỉ có một thử nghiệm sử dụng tất cả các đối tượng này trong khi các thử nghiệm khác chỉ sử dụng một nhóm con nhỏ, bạn sẽ tạo ra dư thừa không cần thiết.
Vấn đề phổ biến này xảy ra khi bạn mong đợi rằng các thử nghiệm trong tương lai có thể cần thiết lập rộng rãi, hoặc khi bạn tiếp tục thêm vào một thiết lập hiện có mà không đánh giá những gì thực sự cần thiết.
Các bài kiểm tra khó hiểu hơn vì chúng chứa bối cảnh không liên quan và chậm hơn để thực hiện vì bạn khởi tạo các đối tượng không được sử dụng.
Mã mẫu
Sai lầm
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());
}
}
Đúng
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);
}
}
Phát hiện
- [x] Tự động
Bạn có thể phát hiện mùi này bằng cách so sánh những gì được thiết lập trong các phương pháp thiết lập với những gì được sử dụng trong mỗi thử nghiệm.
Tìm các bài kiểm tra sử dụng ít hơn 50% các đối tượng được khởi tạo.
Các công cụ phủ sóng mã có thể giúp xác định các đối tượng cài đặt chưa được sử dụng bằng cách hiển thị những phần nào của cài đặt không được thực hiện bởi các bài kiểm tra nhất định.
Nếu bạn thấy mình viết các điều kiện trong thiết lập để tạo các ngữ cảnh khác nhau, đó là một dấu hiệu rõ ràng bạn cần một thiết lập cụ thể về thử nghiệm thay vào đó.
Ngày xưa ️
- thử nghiệm
Mức độ
- [x] trung gian
Tại sao lại quan trọng ️
Mỗi thử nghiệm nên phản ánh một kịch bản thế giới thực cụ thể.
Các thiết lập tràn ngập phá vỡ sự rõ ràng này, làm cho nó khó để xem những gì đang được kiểm tra và làm tăng khả năng mắc lỗi.
Sự phá vỡ nàyBijectionlàm cho các bài kiểm tra khó hiểu hơn vì bạn không thể xác định những khía cạnh nào của thiết lập là quan trọng cho bài kiểm tra và những khía cạnh nào chỉ là tiếng ồn.
Khi một thử nghiệm thất bại, bạn sẽ dành nhiều thời gian hơn để điều tra các phụ thuộc có thể không liên quan đến thất bại.
Kiểm tra trở nên mỏng manh hơn vì các thay đổi đối tượng chưa sử dụng vẫn có thể phá vỡ các thử nghiệm nếu các đối tượng đó tham gia vào quá trình thiết lập.
Thế hệ
Các máy phát mã AI thường tạo ra mùi hôi này khi họ tạo ra các thiết bị kiểm tra toàn diện cố gắng bao gồm tất cả các kịch bản có thể.
Họ ưu tiên tính toàn vẹn hơn tập trung, dẫn đến các phương pháp thiết lập sưng mà bắt đầu nhiều đối tượng hơn là cần thiết cho các thử nghiệm riêng lẻ.
Khám phá
AI có thể phát hiện mùi này với các hướng dẫn đơn giản như "Tối ưu hóa thiết lập thử nghiệm của tôi chỉ để bao gồm những gì cần thiết cho mỗi thử nghiệm."
Các công cụ AI hiện đại có thể so sánh mã thiết lập với việc sử dụng phương pháp thử nghiệm và đề xuất tái tạo nhắm mục tiêu, tách thiết lập được chia sẻ khỏi thiết lập cụ thể cho thử nghiệm.
Hãy thử!
Hãy nhớ: Trợ lý AI làm rất nhiều sai lầm
Suggested Prompt: Break các bài kiểm tra và thiết lập
Suggested Prompt: Break các bài kiểm tra và thiết lập
Without Proper Instructions |
With Specific Instructions |
---|---|
Kết luận
Các thiết lập thử nghiệm quá tải mà bắt đầu các đối tượng chỉ cần bởi một vài thử nghiệm làm cho bộ thử nghiệm của bạn khó hiểu và duy trì hơn.
Khi bạn tạo các thiết lập tập trung chỉ chứa những gì mỗi thử nghiệm cần, bạn cải thiện độ rõ ràng, tốc độ và độ tin cậy của các thử nghiệm của bạn.
Hãy nhớ rằng các bài kiểm tra nhằm mục đích ghi lại hành vi thông qua các ví dụ vàThay thế bình luận.
Quá nhiều bối cảnh không liên quan làm cho những ví dụ đó ít dễ đọc hơn.Thử nghiệm sạch sẽ kể một câu chuyện rõ ràng mà không có sự phân tâm không cần thiết.
Liên hệ
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
Thông tin thêm
Disclaimer
Code Smells là của tôiÝ kiến.
Tín dụng
Hình ảnh byÔng Marcin SimonidesCóUnsplash
Nếu bạn phải tạo nhiều cấu trúc trước khi thử nghiệm, có lẽ bạn đang thử nghiệm quá nhiều lớp
Nếu bạn phải tạo nhiều cấu trúc trước khi thử nghiệm, có lẽ bạn đang thử nghiệm quá nhiều lớp
James bờ biển
Bài viết này là một phần của CodeSmell Series.