[開発図書]Clean Code::9章-ユニットテスト/ドメイン固有言語部分xの理解


テストコードを常にきれいに保つ.そのため、ユニットテストを行う必要があります.

📕 3つのTDD規則


?なぜ実際のコードを書く前に書くのか、なぜですか.実行を失敗させるためにユニットテストを手配しましたか?どうして失敗したの?間違いを修正するためだけ?どうして知らなかったの?
  • で失敗したユニットテストを記述する前に、実際のコードは記述されません.
    :実際のコードを作成する前に、ユニットテストを行います.
  • コンパイルは失敗せず、ユニットテストは失敗の程度でのみ記述されます.
  • では、失敗したテストに合格するまで、実際のコードのみが作成されます.
  • このようにテストコードを作成すると,実際のコードのように膨大な数になるため,管理が重要となる.
    *TDD(Test Driven Development):「テスト主導開発」.反復テストのソフトウェアメソッド論を用いて,反復ステップにより小ユニットテストケースの作成とそのテストに合格したコードの追加を実現した.

    📗 テストコードをきれいに保つ


    実際のコードが変化すると、テストコードも変化するので、正しいテストコードを書くことが重要です.

    📍 テストは柔軟性、保守性、再利用性を提供します。


    柔軟性、保守性、再利用性を提供する部分がユニットテストです.テストケースがある場合は、簡単に変更できます.従って、実際のコードを確認できる自動化ユニットテストキットは、既存の設計をより安全に保持することができる.
    *ユニットテスト:コードを記述する最小ユニットである関数をテストする方法
    *オーバーライド率のテスト:テスト範囲全体でテストを行います(オーバーライド率が100%でない場合は、他のテストケースを設計します).
    *テストキット(Test Suites):テストされたコンポーネントまたはモジュール、システムで使用されるテストケースの集合
    #####テストケース:テストユニットのInputに入ります.または、このテストをn回実行します.(呼び出し関数)
    #####テストユニット:テストに使用するユニット機能.(関数)
    #####テストキット:テストユニットの集合.(関数グループ)

    📙 クリーンなテストコード


    きれいなテストコードを生成するためには,可読性が重要である.そのためには,明確性,単純性,豊かな表現力が必要である.テストコードは最小限の表現で多くのものを表すべきです.
    「BUILD-OPERATE-CHECKモード」は、読みやすさの向上に役立ちます.
  • BUILD:テスト資料を作成する.
  • OPERATE:操作テスト資料.
  • CHECK:操作の結果を確認します.
  • 💻 例



    📍 ドメイン固有の言語->理解できません...


    一般的なシステム操作APIを使用するよりも、API上で関数とユーティリティを実装した後、これらの関数とユーティリティを使用することができるため、テストコードを書くのも読みやすい.このように実装されたユーティリティは,テストコードに用いられる特殊なAPIとなる.すなわち,テストを実現する当事者と後でテストを読む読者を助けるテスト言語である.
    *ドメイン固有言語(DSL):特定のレルムに最適化されたプログラミング言語、特定のレルムまたはドメインの概念とルール、ex)SQLを使用します.データベース内のデータを参照するために発行されるクエリーは、その名の通り「データベース内のデータを参照」にのみ使用され、SQLを使用してWebアプリケーションサーバを作成することは絶対に不可能です.逆にJAVAはSQL(実際にはSQLは特定の構文を持つ文字列)を生成したり、Webアプリケーションサーバを作成したり、他の必要なものを生成したりすることができます.ただ他の分野では、他の言語がもっとよくて、できるかもしれません.
    ??DSLにはJava、、、?ドメイン専用言語は、Java、C、Rubyなどの共通言語よりも複雑です.

    こんなこともあって、どれが正しいのかわからない…!

    📍 ダブルスタンダード


    試験APIコードに適用される基準と実際のコードに適用される基準は明らかに異なる.
    シンプルで簡潔で表現力に富んでいますが、実際のコードのように効率的である必要はありません.実際の環境ではなくテスト環境で実行されるコードなので、実際の環境とテスト環境の要件は異なります.
    💻 Bad Code
    私たちが知りたい事実は「急速に下がっているかどうか」です.
    しかし、次のコードには詳細が多すぎます.
    @Test
    public void turnOnLoTempAlarmAtThreashold() throws Exception {
      hw.setTemp(WAY_TOO_COLD); 
      controller.tic(); 
      assertTrue(hw.heaterState());   
      assertTrue(hw.blowerState()); 
      assertFalse(hw.coolerState()); 
      assertFalse(hw.hiTempAlarm());       
      assertTrue(hw.loTempAlarm());
    }
    💻 Refactoring Code
    上のコードは再設計され,理解しやすい形式に変換された.
    @Test
    public void turnOnLoTempAlarmAtThreshold() throws Exception {
      wayTooCold();
      assertEquals("HBchL", hw.getState()); 
    }
    💻 More Selection
    下図のように、コードの理解度が向上します.
    @Test
    public void turnOnCoolerAndBlowerIfTooHot() throws Exception {
      tooHot();
      assertEquals("hBChl", hw.getState()); 
    }
    @Test
    public void turnOnHeaterAndBlowerIfTooCold() throws Exception {
      tooCold();
      assertEquals("HBchl", hw.getState()); 
    }
    @Test
    public void turnOnHiTempAlarmAtThreshold() throws Exception {
      wayTooHot();
      assertEquals("hBCHl", hw.getState()); 
    }
    @Test
    public void turnOnLoTempAlarmAtThreshold() throws Exception {
      wayTooCold();
      assertEquals("HBchL", hw.getState()); 
    }
    実際の環境では絶対にだめですが、テスト環境では全く問題のない方法があります.これは通常、メモリまたはCPUの効率に関係します.コードの清潔さとは全く関係ありません.

    📍 String, StringBuffer, StringBuilder

  • String
  • の不変性があるため、メモリ使用に効率的です.
  • 文字列を変更すると、新しいStringインスタンスが作成され、初期宣言値はGarbageとして保持され、Garbage Collectionによって削除されます.
  • の不変の文字列を頻繁に読み取る場合に役立ちます.
  • 文字列の追加、修正、削除などの操作を頻繁に行うアルゴリズムでは、Stringクラスを使用するとHip Memoryに大量の一時ゴミ(Garbage)が生成され、Hip Memoryが
  • 未満になる.
  • StringBuffer VS StringBuilder
  • の両者の最大の違いは、同期の一意性にある.
  • StringBufferは同期キーをサポートし、マルチスレッド環境では安全です.
  • StringBuilderは同期をサポートしていないため、マルチスレッド環境での使用には適していませんが、同期を考慮しないため、単一スレッドでのパフォーマンスはStringBufferよりも優れています.
  • 📘 テストごとにassertを1つずつ


  • assert文が一体化した関数は1つの結論しかないので,コードを迅速かつ容易に理解できる.

  • Give-When-Thenモードを使用してテストを2つの部分に分け、各部分でassert文を実行します.
  • public void testGetPageHierarchyAsXml() throws Exception { 
      givenPages("PageOne", "PageOne.ChildOne", "PageTwo");
      
      whenRequestIsIssued("root", "type:pages");
    
      thenResponseShouldBeXML(); 
    }
    public void testGetPageHierarchyHasRightTags() throws Exception { 
      givenPages("PageOne", "PageOne.ChildOne", "PageTwo");
    
      whenRequestIsIssued("root", "type:pages");
    
      thenResponseShouldContain(
        "<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>"
      ); 
    }

  • 利点:コードが毒性/欠点を増加:重複コードを増加

  • Templateメソッドモードによる重複除外
  • は、部分を親クラスに配置し、部分を子クラスに配置するかどうかを指定します.
  • 独立クラスを作成します.

  • ただし、assert文の数は少ないほうがいいです.
  • *assert:開発/テストフェーズでパラメータが正しく遷移したか、または特定のメソッドの実行限界を決定するために使用されます.

    📍 各テストのコンセプト


    コンセプトが混ざっていると、どのようなテストを行うのか理解しにくく、メンテナンスも難しい.
  • 概念のassert文を最小
  • に減らす
  • テスト関数は、1つの概念
  • のみをテストします.

    📒 F.I.R.S.T


    FASTはテストが速いことを説明します.テストは常に最初から問題を見つけます.個々のテストに独立して依存することはできません.各テストは独立しており、任意の順序で実行できます.どの環境でも繰り返し可能である必要があります.自己検証テストはbool値で結果を生成する必要があります.タイムリーなテストはタイムリーに作成しなければならない.ユニットテストは、テストする実際のコードを実装する前に実施される.
    ?ここで、夫-分は私の疑問の部分とつながっていますか?

    🧩 勉強する部分

  • ユニットテスト:https://imasoftwareengineer.tistory.com/88https://sabarada.tistory.com/682
  • Give When-Thenモード
  • String, StringBuffer, StringBuilder
  • 📚 Reference

  • Clean Code:爱子日软件达人精神
  • TDDの意味:https://wooaoe.tistory.com/332
  • DSL : https://unabated.tistory.com/entry/DSLDomain-Specific-Language-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0 , https://lannstark.tistory.com/13
  • assert : https://yeh35.github.io/blog.github.io/documents/java/java-assert/
  • String, StringBuffer, StringBuilder : https://ifuwanna.tistory.com/221