あなたのテスト設定が実際のテストよりも大きい場合
TL;DR: 部分的にのみ使用されているバラバラな設定は、テストをより接続し、理解しにくくなります。
TL;DR: 部分的にのみ使用されているバラバラな設定は、テストをより接続し、理解しにくくなります。
トラブル
- カップリング
- 読解性
- 無駄な執行時間
- 誤解コンテキスト
- 依存症の隠された検査
- より厳しい保守
- Brittle テスト スイート
- 依存性の混乱
- 減速執行
- 誤解文脈
ソリューション↓
- 集中設定方法の作成
- テスト特有のフィギュアを適用
- 最低限のセットアップ
- 工場テスト方法の実施
改装 ️
https://hackernoon.com/improving-the-code-one-line-at-a-time
https://maximilianocontieri.com/refactoring-011-replace-comments-with-tests
コンテキスト
テストを書くとき、さまざまなオブジェクトを初期化する大きな設定方法を作成する場合があります。
1 つのテストのみがこれらのオブジェクトをすべて使用する場合、他のテストは小さなサブセットのみを使用する場合、不要なオーバーヘッドを作成します。
この一般的な問題は、将来のテストが広範なセットアップを必要とする可能性があると予想する場合、または本当に必要なものを評価せずに既存のセットアップに追加し続ける場合に発生します。
テストは、無関係な文脈が含まれているため理解しにくいし、使用されていないオブジェクトを初期化するため実行するのが遅い。
サンプルコード
間違えました↓
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());
}
}
正しい
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);
}
}
検出
- (x)半自動
この臭いは、各テストで使用されたものとセットアップ方法で設定されたものを比較することで検出できます。
初期化されたオブジェクトの50%未満を使用するテストを検索してください。
コードカバーツールは、セットアップのどの部分が特定のテストによって実行されていないかを示すことにより、未使用のセットアップオブジェクトを識別するのに役立ちます。
さまざまな文脈を作成するためにセットアップで条件を書くことがあれば、代わりにテスト特有のセットアップが必要であるという明確な兆候です。
タグ ️
- テスト
レベル
- (x) 中間
なんでバッテリーが大事なの️
それぞれのテストは、特定の現実世界のシナリオを反映する必要があります。
膨張したセットアップはこの明確性を破り、テストされているものを見るのが困難になり、エラーの可能性を高めます。
この壊れたバイエクションテストの設定のどの側面がテストにとって重要で、どの側面が単なる騒音であるかを判断できないため、テストを理解するのが難しくなります。
テストが失敗すると、失敗に関連しない可能性のある依存症を調査するのにより多くの時間を費やします。
未使用のオブジェクトへの変更が、これらのオブジェクトがセットアッププロセスに参加する場合でもテストを破ることができるため、テストはより脆弱になります。
世代
AI コード ジェネレータは、可能なすべてのシナリオをカバーしようとする包括的なテスト フィギュアを生成するときにしばしばこの臭いを作成します。
彼らはフォーカスよりも完全性を優先し、個々のテストに必要以上に多くのオブジェクトを初期化する膨張した設定方法を生み出します。
検出 ↓↓
AIはこの臭いを「テスト設定を最適化して、各テストに必要なものだけを含める」などの簡単な指示で検出することができます。
現代のAIツールは、テスト方法の使用とセットアップコードを比較し、ターゲット変更を提案し、共有セットアップとテスト特有のセットアップを分離することができます。
ぜひお試しください!↓↓
注意:AIアシスタントは多くの間違いを犯す
Suggested Prompt: Break the tests and the setup(テストとセットアップを破る)
Suggested Prompt: Break the tests and the setup(テストとセットアップを破る)
Without Proper Instructions |
With Specific Instructions |
---|---|
結論 ↓
いくつかのテストで必要なオブジェクトを初期化する過剰なテスト設定は、テスト スイートを理解し、維持するのが難しくなります。
各テストに必要なものだけを含む集中設定を作成すると、テストの明確性、速度、信頼性を向上させます。
テストは、例を通じて行動を文書化することを目的とし、コメントの置き換え.
あまりにも無関係な文脈は、これらの例を読めないようにします. Clean tests tell a clear story without unnecessary distractions. クリーンテストは、不要な分散なしに明確なストーリーを語ります。
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
詳細情報↓↓
ディスカレーター
Code Smells Are Mine(コードの匂いは私のもの)意見.
クレジット↓
写真 byマルチン・サイモニデスはUnsplashについて
テストの前に多くの構造を作成する必要がある場合は、多分多くの層をテストしているかもしれません。
テストの前に多くの構造を作成する必要がある場合は、多分多くの層をテストしているかもしれません。
ジェームズ・シャー
この記事は CodeSmell シリーズの一部です。