CleanCode第9章ユニットテスト

27811 ワード

エイズとTDDのおかげで、自動化ユニットテストのプログラマーが増え、ますます多くの傾向があります.
良好なテスト例を作成することは非常に重要です.

1.三つのTDD法則

  • で失敗したユニットテストを記述する前に、実際のコードは記述されません.
  • コンパイルは失敗せず、ユニットテストは失敗の程度でのみ記述されます.
  • では、失敗したテストに合格するまで、実際のコードのみが作成されます.
  • このようにコードを記述すると,開発とテストサイクルは約30秒に制限される.
    ->毎日数十個、毎月数百個、毎年数千個のテストケースがあります.
    (実際のコードに相当する膨大なテストコードは管理上の問題を引き起こす.)

    2.清潔なテストコードを保持する


    テストコードが不潔な場合は、次のことが起こります.
    リリース
  • の新しいバージョンごとに、チームのテストケースのメンテナンスとメンテナンスのコストが
  • 増加します.
  • の開発者の間では、テストコードが最大の不満となっています.
  • の管理者が予測値が大きすぎる理由を尋ねると、チームはテストコードを非難します.
  • テストキットを廃棄します.
  • のテストコードがないため、開発者は修正後のコードが正しいかどうかを確認できません.
  • 欠陥率が向上します.
  • 予期せぬ欠陥数が増加すると、開発者は変更をためらうことになります.
  • を変更すると、弊害が利益より大きいと判断され、コードは整理されません.
  • コードが破壊されます.
  • テストキットがなく、混在したコード、挫折した顧客とテストの努力が無駄になり、失望感だけが残った.
  • ->テストケースは、開発の柔軟性、保守性、再利用性を提供するために簡単に変更できます.

    3.クリーンなテストコード


    きれいなテストコードを生成するために、
    明確性、単純性、豊かな表現力が必要
    
    public void testGetPageHieratchyAsXml() throws Exception {
    	crawler.addPage(root, PathParser.parse("PageOne"));
    	crawler.addPage(root, PathParser.parse("PageOne.ChildOne"));
    	crawler.addPage(root, PathParser.parse("PageTwo"));
    
    	request.setResource("root");
    	request.addInput("type", "pages");
    	Responder responder = new SerializedPageResponder();
    	SimpleResponse response =
    		(SimpleResponse) responder.makeResponse(new FitNesseContext(root), request);
    	String xml = response.getContent();
    
    	assertEquals("text/xml", response.getContentType());
    	assertSubString("<name>PageOne</name>", xml);
    	assertSubString("<name>PageTwo</name>", xml);
    	assertSubString("<name>ChildOne</name>", xml);
    }
    
    public void testGetPageHieratchyAsXmlDoesntContainSymbolicLinks() throws Exception {
    	WikiPage pageOne = crawler.addPage(root, PathParser.parse("PageOne"));
    	crawler.addPage(root, PathParser.parse("PageOne.ChildOne"));
    	crawler.addPage(root, PathParser.parse("PageTwo"));
    
    	PageData data = pageOne.getData();
    	WikiPageProperties properties = data.getProperties();
    	WikiPageProperty symLinks = properties.set(SymbolicPage.PROPERTY_NAME);
    	symLinks.set("SymPage", "PageTwo");
    	pageOne.commit(data);
    
    	request.setResource("root");
    	request.addInput("type", "pages");
    	Responder responder = new SerializedPageResponder();
    	SimpleResponse response =
    		(SimpleResponse) responder.makeResponse(new FitNesseContext(root), request);
    	String xml = response.getContent();
    
    	assertEquals("text/xml", response.getContentType());
    	assertSubString("<name>PageOne</name>", xml);
    	assertSubString("<name>PageTwo</name>", xml);
    	assertSubString("<name>ChildOne</name>", xml);
    	assertNotSubString("SymPage", xml);
    }
    
    public void testGetDataAsHtml() throws Exception {
    	crawler.addPage(root, PathParser.parse("TestPageOne"), "test page");
    
    	request.setResource("TestPageOne"); request.addInput("type", "data");
    	Responder responder = new SerializedPageResponder();
    	SimpleResponse response =
    		(SimpleResponse) responder.makeResponse(new FitNesseContext(root), request);
    	String xml = response.getContent();
    
    	assertEquals("text/xml", response.getContentType());
    	assertSubString("test page", xml);
    	assertSubString("<Test", xml);
    }
    これは悪いテストコードです.
  • addPageとassertSubStringを呼び出すために、重複するコードがたくさんあります.
  • PathParserはネットワークロボットが使用する対象であり,テストに関係なく意図的にぼやけているだけである.
  • 応答オブジェクトのコードと応答を収集し変換するコードが生成されなくても、関係ありません.
  • がコードを理解するまで、テストケースを理解しませんでした.
  • こちらはBUILD-OPERATE-CHECKモードに適しています
    1.テスト資料を作成する.
    2.操作テスト資料.
    3.操作結果が正しいか確認する.
    public void testGetPageHierarchyAsXml() throws Exception {
    	makePages("PageOne", "PageOne.ChildOne", "PageTwo");
    
    	submitRequest("root", "type:pages");
    
    	assertResponseIsXML();
    	assertResponseContains(
    		"<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>");
    }
    
    public void testSymbolicLinksAreNotInXmlPageHierarchy() throws Exception {
    	WikiPage page = makePage("PageOne");
    	makePages("PageOne.ChildOne", "PageTwo");
    
    	addLinkTo(page, "PageTwo", "SymPage");
    
    	submitRequest("root", "type:pages");
    
    	assertResponseIsXML();
    	assertResponseContains(
    		"<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>");
    	assertResponseDoesNotContain("SymPage");
    }
    
    public void testGetDataAsXml() throws Exception {
    	makePageWithContent("TestPageOne", "test page");
    
    	submitRequest("TestPageOne", "type:data");
    
    	assertResponseIsXML();
    	assertResponseContains("test page", "<Test");
    }

    4.テストごとにassertを1つずつ


    assert文が1の関数であるという結論は分かりやすく、迅速なコードですが、上記の例では「XMLとして出力」されたassert文と「特定の文字列を含む」assert文を1つにまとめています.
    切り裂くと下と同じです.
    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 METHODを使うとより見やすくなります.
    (与えられた/時間の部分を親に配置)
    (サブクラスに一部を配置)

    5.F.I.R.S.T—5つのクリーンなテストルール

  • 高速:テストが高速です.
  • テストは速く回転します.
    -テストが遅かったら
    -頻繁に回らないと、最初から問題が見つからず、直らない.
    -コードを整理することもできません.
    -最終コード品質が低下し始めました.
  • 独立:各テストは相互に依存できません.
  • 1つの
  • テストは、次のテストの実行環境に備えるべきではありません.
    -各テストは独立して実行され、任意の順序で実行できます.
    -テストが互いに依存している場合、1つの失敗が続くと、残りも失敗し続けるため、原因の診断が難しく、後続のテストで見つけなければならない欠陥が隠されています.
  • 再現性:テストは任意の環境で再現できる必要があります.
  • は、実環境、QA環境、バスで帰宅途中に使用するノートパソコン環境でも動作可能でなければならない.
    -テストが実行できない環境がある場合は、テストが失敗した理由を説明する口実が発生します.
    -環境がサポートされていないため、テストを実行できない場合があります.
  • ワード検証:テストはBool値で結果を生成する必要があります.成功か失敗か.
  • ユーザーにログファイルを読み込ませて、
  • を通過したかどうかを判断することはできません.
    -パスするかどうかを確認するために、2つのテキストファイルを手動で比較することはできません.
    -テストが成功と失敗を自分で測らないと、判断が主観的になり、冗長な手作業で評価する必要があります.
  • タイムリー:テストはタイムリーに記入しなければならない.
  • ユニットテストは、テストする実際のコードを実装する前に実施される.
    -実際のコードを実装した後にテストコードを作成すると、実際のコードはテストしにくいことがわかります.
    -一部の実際のコードはテストが難しいと判定される可能性があります.
    -実際のコードをテストできないように設計するかもしれません.