SlideShare a Scribd company logo
1 of 58
Download to read offline
Chapter 19.
xUnit Basic
 Patterns
●   Test Definition
    ●   Test Method
        –   Four-Phase Test
    ●   Assertion Method
        –   Assertion Message
    ●   Testcase Class
●   Test Execution
    ●   Test Runner
    ●   Testcase Object
    ●   Test Suite Object
    ●   Test Discovery
    ●   Test Enumeration
    ●   Test Selection
Test Method
How It Works(1)
●   テストコードってどこに書くの?
    ●   ひとつひとつのテスト毎にメソッド (Test Method) にし
        てクラスに配置しましょう
●   How It Works
    ●   各テストをメソッド/手続き/関数のかたちで Four-
        Phase(358) の実装を行い、Fully Automated Test と
        する。
    ●   大事なのは、 assertion を書いて自己テストコード
        (Self-Checking Test:26) とすること
How It Works(2)
●   Test Method には標準 Template がある
    ●   Simple Success Test
        –   正常系のテスト。 Fixture setup から result verification ま
            で一本道
    ●   Expected Exception Test
        –   例外系のテスト
    ●   Constructor Test
        –   オブジェクトを作成し属性をテストするだけのテスト
Why We Do This
●   手続き型言語の場合
    ●   Test Method をファイルやモジュールに書く
●   オブジェクト指向言語の場合
    ●   Test Method を Testcase Class(373) の中に書
        き、Test Discovery(393) や Test Enumeration(399)
        を使って Test Method を Testcase Object(382) とし
        てインスタンス化する
    ●   標準 template に従うことでテストを読みやすくシンプ
        ルにし、 SUT の動くドキュメントとすることができる
Implementation Notes
●   どう仕組みを実装する?
    ●   Static method として実装し呼び出しを列挙する
        –   テスト結果を集めたりする共通化がやりにくい
    ●   Test Method 一つ一つに対応する Test Suite
        Object(387)をつくる
        –   Test Discovery や Test Enumaration でインスタンス化する
            場合に便利
    ●   静的型付け言語の場合はメソッドに “throws
        Exception” などを書かなければならない
        –   コンパイラに対して「この例外は Test Runner が処理するよ」
            という意思表示になる
    ●   殆どの Test Method は3パターンに分類できる
Simple Success Test
●   ソフトウェアには正常系 “happy path” があ
    る。Simple Success Test はそれを書く
    ●   SUT をインスタンス化して叩き、結果を assert
        –   言い換えると、 Four-Phase に則ったテストを書く
    ●   例外はキャッチせず、 Test Automation Framework ま
        で貫通させる
        –   テストの中で例外を扱うと Obscure Test や誤解のもと
        –   Tests as Documentation の原則を思い出そう
        –   Try-catch を書かない利点は他にもあって、 Test
            Automation Framework が例外発生行を特定しやすくなる
            こと
Simple Success Test のダメな例

public void testFlightMileage_asKm() throws Exception {
  //set up fixture
  Flight newFlight = new Flight(validFlightNumber);
  try {
    //exercise SUT
    newFlight.setMileage(1122);
                    
    //verify results
    int actualKilometres = newFlight.getMileageAsKm();
    int expectedKilometres = 1810;
    //verify results
    assertEquals( expectedKilometres, actualKilometres);
  } catch (InvalidArgumentException e) {
    fail(e.getMessage());
  } catch (ArrayStoreException e) {
    fail(e.getMessage());
  }
}



                                                      不要な try/catch
Simple Success Test の良い例

public void testFlightMileage_asKm() throws Exception {
  //set up fixture
  Flight newFlight = new Flight(validFlightNumber);
  newFlight.setMileage(1122);
  //exercise mileage translator
  int actualKilometres = newFlight.getMileageAsKm();
                   
  //verify results
  int expectedKilometres = 1810;
  assertEquals( expectedKilometres, actualKilometres);
}




                                          xUnit は unexpected exception を
                                                失敗として扱えるので、
                                           throws Exception しておけばよい
Expected Exception Test (1)
●   多くの不具合は正常系以外のパスに潜む。特に例
    外系のシナリオ。それは、
    ●   Untested Requirements (268) や、
    ●   Untested Code (268) であったりするため
●   Expected Exception Test はわざと SUT が例外
    を出すようなテストを書き、きちんと例外が出ること
    を調べる
    ●   例外の中身も調べたいときは Equality Assertion で調
        べる
    ●   例外が出なかったときは fail メソッドなどでテストを失
        敗させる
Expected Exception Test (2)
●   想定される (expected) 例外には、テストを書いた
    ほうがよい
●   再現が難しいが、出るかもしれない (might raise)
    例外には、テストを書かなくてよい
    ●   (★ ネットワーク障害とか、 Disk full とか)
    ●   そういう例外は Simple Success Test の失敗として現
        れるため
    ●   もしそういう例外もテストしたいなら、 Test Stub から例
        外を発生させてテストする
Expected Exception Test (3)
●   例外をテストするときの仕組み
    ●   JUnit 3.x
        –   ExpectedException クラスを継承させる (?)
             ●   小さいテストクラスが沢山できるし、あまり旨味は無い
    ●   JUnit 4.x, NUnit
        –   Test Method の annotation/attribute に書く
    ●   Block のある言語 (Smalltalk, Ruby, …)
        –   Block で例外が発生するか調べるテストを書ける
Expected Exception Test のダメな例

public void testSetMileage_invalidInput() throws Exception {
  //set up fixture
  Flight newFlight = new Flight(validFlightNumber);
  //exercise SUT
                   
  newFlight.setMileage(-1122); //invalid
  //how do we verify an exception was thrown?
}




                                      想定された例外なのにテストが失敗してしまう
Expected Exception Test の良い例

public void testSetMileage_invalidInput()throws Exception {
  //set up fixture
  Flight newFlight = new Flight(validFlightNumber);
  try {
                    
    //exercise SUT
    newFlight.setMileage(-1122);
    fail("Should have thrown InvalidInputException");
  } catch(InvalidArgumentException e) {
    //verify results
    assertEquals( "Flight mileage must be positive", e.getMessage());
  }
}




                                                  想定される例外を catch する
                                                 例外が出なかった場合は fail させる
Expected Exception Test の特殊例?
public void testSetMileage_invalidInput2() throws Exception {
  //set up fixture
  Flight newFlight = new Flight(validFlightNumber);
  try {
    //exercise SUT
    newFlight.setMileage(-1122);
                    
    //cannot fail() here if SUT throws same kind of exception
  } catch(AssertionFailedError e) {
    //verify results
    assertEquals( "Flight mileage must be positive", e.getMessage());
    return;
  }
  fail("Should have thrown InvalidInputException");
}




     Fail メソッドが投げる例外と同じ例外を SUT が投げる場合にはこう書くしかない !?
Method Attribute を使った EET
[Test]
[ExpectedException(typeof( InvalidArgumentException),
                       "Flight mileage must be > zero")]
public void testSetMileage_invalidInput_AttributeWithMessage() {
  //set up fixture
  Flight newFlight = new Flight(validFlightNumber);
                   
  //exercise SUT
  newFlight.setMileage(-1122);
}
Block を使った EET
Smalltalk:

testSetMileageWithInvalidInput
 self
  should: [Flight new mileage: -1122]
  raise: RuntimeError new 'Should have raised error'


Ruby:

def testSetMileage_invalidInput
 flight = Flight.new()
 assert_raises( RuntimeError, "Should have raised error") do
   flight.setMileage(-1122)
 end
end
Rspec でやってみる
describe Flight do
 before do
  @flight = Flight.new
 end

 it "Should have raised error" do
   lambda {
     flight.setMileage(-1122)
   }.should_raise(RuntimeError)
 end

end
Constructor Test
●   Fixture Setup phase で作成されたオブジェクトが
    正しく作成されているかを各 Test Method で調べ
    ていると Test Code Duplication (213) がひどく
    なる
    ●   オブジェクト作成のテストだけ別の Test Method にす
        ることで他のテストをシンプルにすることができる
    ●   Defect Localication にもなる
        –   各属性の Test Method を分けるとさらに Defect
            Localization
    ●   Constructor Test は Simple Success Test の形をと
        る場合もあるし、 Expected Exception Test の形をと
        る場合もある
Dependency Initialization Test
●   Constructor Test の亜種
●   置き換え可能な依存があるオブジェクトがある場
    合、本番環境では本物の依存オブジェクトが参照さ
    れることをテストする
●   ふつうの Constructor Test と分けて管理した方
    がよい
Constructor Test のダメな例
public void testFlightMileage_asKm2() throws Exception {
  //set up fixture
  //exercise constructor
  Flight newFlight = new Flight(validFlightNumber);
  //verify constructed object
  assertEquals(validFlightNumber, newFlight.number);
  assertEquals("", newFlight.airlineCode);
  assertNull(newFlight.airline);
  //set up mileage    
  newFlight.setMileage(1122);
  //exercise mileage translator
  int actualKilometres = newFlight.getMileageAsKm();
  //verify results
  int expectedKilometres = 1810;
  assertEquals( expectedKilometres, actualKilometres);
  //now try it with a canceled flight
  newFlight.cancel();
  try {
    newFlight.getMileageAsKm();
    fail("Expected exception");
  } catch (InvalidRequestException e) {
    assertEquals( "Cannot get cancelled flight mileage", e.getMessage());
  }
}
                                                    いろいろやりすぎでピントがあっていない
                                                           Eager Test
Constructor Test の良い例1 正常系
public void testFlightConstructor_OK() throws Exception {
  //set up fixture
  //exercise SUT
  Flight newFlight = new Flight(validFlightNumber);
  //verify results
  assertEquals(   
                 validFlightNumber, newFlight.number );
  assertEquals( "", newFlight.airlineCode );
  assertNull( newFlight.airline );
}
Constructor Test の良い例2 異常系
public void testFlightConstructor_badInput() {
  //set up fixture
  BigDecimal invalidFlightNumber = new BigDecimal(-1023);
  //exercise SUT
  try {
    Flight newFlight = new Flight(invalidFlightNumber);
                     
    fail("Didn't catch negative flight number!");
  } catch (InvalidArgumentException e) {
    //verify results
    assertEquals( "Flight numbers must be positive", e.getMessage());
  }
}
Constructor Test があると他のテストの
        ピントがはっきりする
 public void testFlightMileage_asKm() throws Exception {
   //set up fixture
   Flight newFlight = new Flight(validFlightNumber);
   newFlight.setMileage(1122);
                    
   //exercise mileage translator
   int actualKilometres = newFlight.getMileageAsKm();
   //verify results
   int expectedKilometres = 1810;
   assertEquals( expectedKilometres, actualKilometres);
 }




                                      インスタンス化直後の assertion は不要
Four-Phase Test
●   Test Definition
    ●   Test Method
        –   Four-Phase Test
    ●   Assertion Method
        –   Assertion Message
    ●   Testcase Class
●   Test Execution
    ●   Test Runner
    ●   Testcase Object
    ●   Test Suite Object
    ●   Test Discovery
    ●   Test Enumeration
    ●   Test Selection
Four-Phase Test
●   良いテストには4つの Phase がある
    ●   Setup
    ●   Exercise
    ●   Verify
    ●   Teardown
●   ワンパターン = テストの読みやすさ = Tests as
    Documentation
    ●   Test Method の中身はテスト内容に集中すべし
Four-Phase Test
●   どう setup/teardown する?
    ●   Testcase Class per Class または Testcase Class
        per Feature の場合
        –   In-line Setup
        –   Garbase-Collected Teardown または In-line Teardown
    ●   Testcase per Fixture の場合
        –   Implicit Setup
             ●   例えば setUp メソッド
        –   Implicit Teardown
             ●   例えば tearDown メソッド
例: Four-Phase Test (In-line)

public void testGetFlightsByOriginAirport_NoFlights_inline() throws Exception {
  //Fixture setup
  NonTxFlightMngtFacade facade =new NonTxFlightMngtFacade();
  BigDecimal airportId = facade.createTestAirport("1OF");
  try {
    //Exercise system
    List flightsAtDestination1 =
      facade.getFlightsByOriginAirport(airportId);
    //Verify outcome
    assertEquals( 0, flightsAtDestination1.size() );
  } finally {
    //Fixture teardown
    facade.removeAirport(airportId );
  }
}
例: 4PT (Implicit Setup/Teardown)
NonTxFlightMngtFacade facade = new NonTxFlightMngtFacade();
private BigDecimal airportId;

protected void setUp() throws Exception {
  //Fixture setup
  super.setUp();
  airportId = facade.createTestAirport("1OF");
}

public void testGetFlightsByOriginAirport_NoFlights_implicit() throws Exception {
  //Exercise SUT
  List flightsAtDestination1 = facade.getFlightsByOriginAirport(airportId);
  //Verify outcome
  assertEquals( 0, flightsAtDestination1.size() );
}

protected void tearDown() throws Exception {
  //Fixture teardown
  facade.removeAirport(airportId);
  super.tearDown();
}
Assertion Method
●   Test Definition
    ●   Test Method
        –   Four-Phase Test
    ●   Assertion Method
        –   Assertion Message
    ●   Testcase Class
●   Test Execution
    ●   Test Runner
    ●   Testcase Object
    ●   Test Suite Object
    ●   Test Discovery
    ●   Test Enumeration
    ●   Test Selection
Assertion Method
●   どうやってテストに自己チェックさせるか
    ●   ユーティリティメソッドを呼ぶことで望んだ結果になった
        かどうかの評価をすればいい
●   Fully Automated Tests(26)の肝は、テストをSelf-
    Checking Tests(26)にすること
    ●   そのためには期待する結果を表現し、自動でチェックす
        ることが必要
●   Assertion Method は期待する結果を表現し、
    ●   コンピュータにとっては実行可能に
    ●   人間には Tests as Documentation(23) になる。
Why We Do This
●   期待する結果を Conditional Test Logic(200)で
    表現すると…
    ●   饒舌に過ぎ、読むのも理解するのも難しい
    ●   Test Code Duplication(213)が発生しやすい
    ●   Buggy Test(260)も発生しやすい
●   Assetion Method はこの問題を…
    ●   再利用性の高い Test Utility Methods(599)に複雑さ
        を移すことにより解決する
    ●   そのメソッドの正しさは Test Utility Tests(599)でテス
        トすることも可能
まずはひどい例から
if (x.equals(y)) {
    throw new AssertionFailedError(
        "expected: <" + x.toString() +
        "> but found: <" + y.toString() + ">");
} else { // Okay, continue
    // ...
}

// 上の例では NPE が発生するのでガード節を入れてみると…
                      
if (x == null) { //cannot do null.equals(null)
    if (y == null ) { //they are both null so equal
        return;
    } else {
        throw new AssertionFailedError(
           "expected null but found: <" + y.toString() +">");
    }
} else if (!x.equals(y)) { //comparable but not equal!
    throw new AssertionFailedError(
            "expected: <" + x.toString() +
            "> but found: <" + y.toString() + ">");
} //equal
JUnit はこうリファクタリングした
/**
 * Asserts that two objects are equal. If they are not,
 * an AssertionFailedError is thrown with the given message.
 */
static public void assertEquals(String message,
                          Object expected,
                          Object actual) {
   if (expected == null &&actual == null)
       return;    
   if (expected != null && expected.equals(actual))
       return;
   failNotEquals(message, expected, actual);
}


---------------------------------


assertEquals( x, y ); // 呼び出し側はこれだけ!!
Implementation Notes
●   全ての xUnit ファミリーは Assetion Method を備
    えているが、考えるべきことはある
    ●   Assertion Method をどうやって呼ぶか
    ●   最適な Assertion Method をどう選ぶか
    ●   Assertion Message(370) に何を書くか
Calling Built-in Assertion Methods
●   Test Method(348) からテストフレームワーク組み
    込みで提供されている Assertion Method を呼ぶ
    には…
    ●   フレームワークが提供する Testcase Superclass(638)
        を継承する (JUnit タイプ)
    ●   グローバルクラス/モジュールを完全修飾名で呼び出す
        (NUnit タイプ)
    ●   Mixin (Test::Unit タイプ)
    ●   マクロ (CppUnit タイプ)
Assertion Messages
●   テスト失敗時の出力に含めるメッセージ
    ●   どのテストが失敗したかをわかりやすくする
        –   Assertion Roulette 参照
    ●   省略可能な引数として Assertion Method に渡すかた
        ちが多い
●   テスト失敗時に「なぜ失敗したか」の情報が多けれ
    ばデバッグは容易になる
    ●   正しい Assertion Method を選ぶことはエラー時のメッ
        セージ出力を適切にする意味でも重要
●   問題なのは、 Assertion Message の引数の順番
    が xUnit 毎にブレていること
Choosing the Right Assertion
●   Assertion Method には二つのゴールがある
    ●   期待しない結果のときにはテストを失敗させること
    ●   SUT がどう振る舞うかのドキュメントになること
●   これらのゴールを満たすため、最適な Assertion
    Method を選ぶことが重要になる
●   Assertion Method には以下のカテゴリがある
    ●   Single-Outcome Assertions
    ●   Stated Outcome Assertions
    ●   Expected Exception Assertions
    ●   Equality Assertions
    ●   Fuzzy Equality Assertions
Equality Assertion
●   結果が期待値と等価かどうかを調べる
    ●   もっとも使われる Assertion Method
●   引数の順番は規約としては expected, actual の
    順番
    ●   順番は失敗時のメッセージに関係するので重要
    ●   ★この順番でない xUnit もある。(NUnit とか)
    ●   ★自分の使う Equality Assertion の順を覚えよう
●   内部では等価性を調べるメソッドが呼ばれる
    ●   Java では equals とか
    ●   SUT ごと調べたい場合は Test-Specific Subclass
Equality Assertion いろいろ
assertEquals( expected, actual ); // since JUnit3.x

assertThat( actual, is(expected) ); // since JUnit 4.4

Assert.AreEqual( actual, expected ); // NUnit

is( actual, expected );   // Test::Simple (Perl)

equals( actual,    
                expected ); // QUnit

actual.should == expected // Rspec
Fuzzy Equality Assertion
●   結果と期待値との完全な一致が難しい場合
    ●   浮動小数点をあつかうとき
    ●   期待値と完全一致させるには結果に本質的でない不要
        なゴミが多いとき (XML の空白ノードとか)




assertEquals( 3.1415, diameter/2/radius, 0.001);

assertEquals( expectedXml, actualXml, elementsToCompare );
Stated Outcome Assertion
●   期待値を渡す必要がないとき
●   Conditional Test Logic を避けるためのガード節
    としても使える



assertNotNull(a );

assertTrue(b > c );

assertNonZero(b );
Expected Exception Assertion
●   ブロックやクロージャを備えている言語は発生する
    であろう例外をパラメータとして渡す Assertion
    Method が使える

self
  should: [Flight new mileage: -1122]
  raise: RuntimeError new 'Should have raised error'


assert_raises( RuntimeError,
            "Should have raised error")
            { flight.setMileage(-1122) }


assert_raises( RuntimeError, "Should have raised error")do
  flight.setMileage(-1122)
end
Single-Outcome Assertion
●   常に同じ振る舞いをする Assertion Method
    ●   例えば fail メソッド
●   使われる状況
    ●   まだ完成していないテスト Unfinished Test Assertion を示す
    ●   Expected Exception Test の中の try/catch ブロックで使う



fail( "Expected an exception" );


unfinishedTest();
Single-Outcome Assertion の例

public void testSetMileage_invalidInput()throws Exception {
  //set up fixture
  Flight newFlight = new Flight(validFlightNumber);
  try {
                    
    //exercise SUT
    newFlight.setMileage(-1122);
    fail("Should have thrown InvalidInputException");
  } catch(InvalidArgumentException e) {
    //verify results
    assertEquals( "Flight mileage must be positive", e.getMessage());
  }
}




                                                  想定される例外を catch する
                                                 例外が出なかった場合は fail させる
Assertion Message
●   Test Definition
    ●   Test Method
        –   Four-Phase Test
    ●   Assertion Method
        –   Assertion Message
    ●   Testcase Class
●   Test Execution
    ●   Test Runner
    ●   Testcase Object
    ●   Test Suite Object
    ●   Test Discovery
    ●   Test Enumeration
    ●   Test Selection
Assertion Message
●   どの Assertion Method が落ちたか知りたい
    ●   Assertion Method 毎にメッセージ引数を渡す
●   テスト失敗時の出力に含めるメッセージ
    ●   どのテストが失敗したかをわかりやすくする
        –   Assertion Roulette 参照
    ●   省略可能な引数として Assertion Method に渡すかた
        ちが多い
When to Use It
●   二つの学派(School)がある
    ●   テスト駆動派 (Test drivers) と、その他派
●   テスト駆動派
    ●   “single assertion per Test Method”
    ●   Test Method に Assertion Method がひとつしかな
        いので、どの Assertion Method が落ちたかは自明。
        故に Assertion Message は不要。
●   その他派
    ●   ひとつの Test Method に複数 Assertion Method が
        あるので、 Assertion Message を使いたくなる
Implementation Notes
●   Assertion Message に何を書くべきか
    ●   Assertion-Identifying Message
    ●   Expectation-Describing Message
    ●   Argument-Describing Message
Assertion-Identifying Message
●   同種の Assertion Method が複数ある場合にどれ
    が失敗したか分かりにくい
    ●   Assertion Method 毎に違う文字列を渡して識別する
●   識別に使う文字列の例
    ●   Assertion Method に使う変数名
        –   名前に悩む必要がないので便利かも
    ●   単なる連番
        –   テストコードを読まないと結局どこが失敗したかわからなかっ
            たりする
Expectation-Describing Message
●   テストが失敗した時に「実際何が起こったか」はわ
    かる。だが「何が起こるべきだったか」はわからな
    い。
    ●   テストコード内にコメントを書く手もある
●   もっと良いのは Assertion Message に期待値の
    説明を書くこと
    ●   Equality Assertion の場合は自動でやってくれるので
        必要無し
    ●   Stated Outcome Assertion の場合は入れなければ
        わからない
Argument-Describing Message
●   いくつかの Assertion Method は失敗時の出力が
    不親切
    ●   特に Stated Outcome Assertion
        –   assertTrue(式) とか
        –   失敗したのはわかるが、どんな式が失敗したのかわからない
        –   式を Assertion Message に含めてしまう手がある
Argument-Describing Messageの例

assertTrue( "Expected a > b but a was '" + a.toString() +
         "' and b was '" + b.toString() + "'", a.gt(b) );

assertTrue( "Expected b > c but b was '" + b.toString() +
         "' and    '" + c.toString + "'", b > c ); }
               c was
ご清聴
 ありがとう
ございました

More Related Content

What's hot

Ruby初級者向けレッスン 第46回 ─── Test::Unit
Ruby初級者向けレッスン 第46回 ─── Test::UnitRuby初級者向けレッスン 第46回 ─── Test::Unit
Ruby初級者向けレッスン 第46回 ─── Test::Unit
higaki
 
111008 silverlight square_datavalidation
111008 silverlight square_datavalidation111008 silverlight square_datavalidation
111008 silverlight square_datavalidation
Takayoshi Tanaka
 

What's hot (16)

Apache Wicketのユニットテスト機能
Apache Wicketのユニットテスト機能Apache Wicketのユニットテスト機能
Apache Wicketのユニットテスト機能
 
ソフトウェア・テスト入門5
ソフトウェア・テスト入門5ソフトウェア・テスト入門5
ソフトウェア・テスト入門5
 
xUnit Test Patterns - Chapter11
xUnit Test Patterns - Chapter11xUnit Test Patterns - Chapter11
xUnit Test Patterns - Chapter11
 
モデル検査入門 #wacate
モデル検査入門 #wacateモデル検査入門 #wacate
モデル検査入門 #wacate
 
Introduction to boost test
Introduction to boost testIntroduction to boost test
Introduction to boost test
 
20161203 selenium adventcalender
20161203 selenium adventcalender20161203 selenium adventcalender
20161203 selenium adventcalender
 
ゆもつよ博士論文説明資料公開
ゆもつよ博士論文説明資料公開ゆもつよ博士論文説明資料公開
ゆもつよ博士論文説明資料公開
 
Ruby初級者向けレッスン 第46回 ─── Test::Unit
Ruby初級者向けレッスン 第46回 ─── Test::UnitRuby初級者向けレッスン 第46回 ─── Test::Unit
Ruby初級者向けレッスン 第46回 ─── Test::Unit
 
Code complete ch22_developper_test
Code complete ch22_developper_testCode complete ch22_developper_test
Code complete ch22_developper_test
 
111008 silverlight square_datavalidation
111008 silverlight square_datavalidation111008 silverlight square_datavalidation
111008 silverlight square_datavalidation
 
xUTP Chapter26. Dependency Injection
xUTP Chapter26. Dependency InjectionxUTP Chapter26. Dependency Injection
xUTP Chapter26. Dependency Injection
 
Sencha ug3 siesta_share
Sencha ug3 siesta_shareSencha ug3 siesta_share
Sencha ug3 siesta_share
 
第4回勉強会 単体テストのすすめ
第4回勉強会 単体テストのすすめ第4回勉強会 単体テストのすすめ
第4回勉強会 単体テストのすすめ
 
はこだてIKA 第4回勉強会 単体テスト
はこだてIKA 第4回勉強会 単体テストはこだてIKA 第4回勉強会 単体テスト
はこだてIKA 第4回勉強会 単体テスト
 
Inside Frontend 2 #insideFE
Inside Frontend 2 #insideFEInside Frontend 2 #insideFE
Inside Frontend 2 #insideFE
 
TDDワークショップ(第2回)
TDDワークショップ(第2回)TDDワークショップ(第2回)
TDDワークショップ(第2回)
 

Viewers also liked

Viewers also liked (6)

Introduction to Unit Testing
Introduction to Unit TestingIntroduction to Unit Testing
Introduction to Unit Testing
 
ASP.NET Core 2.0 - .NET São Paulo - Outubro-2017
ASP.NET Core 2.0 - .NET São Paulo - Outubro-2017ASP.NET Core 2.0 - .NET São Paulo - Outubro-2017
ASP.NET Core 2.0 - .NET São Paulo - Outubro-2017
 
Using xUnit as a Swiss-Aarmy Testing Toolkit
Using xUnit as a Swiss-Aarmy Testing ToolkitUsing xUnit as a Swiss-Aarmy Testing Toolkit
Using xUnit as a Swiss-Aarmy Testing Toolkit
 
Mini training - Moving to xUnit.net
Mini training - Moving to xUnit.netMini training - Moving to xUnit.net
Mini training - Moving to xUnit.net
 
Le Tour de xUnit
Le Tour de xUnitLe Tour de xUnit
Le Tour de xUnit
 
AI and Machine Learning Demystified by Carol Smith at Midwest UX 2017
AI and Machine Learning Demystified by Carol Smith at Midwest UX 2017AI and Machine Learning Demystified by Carol Smith at Midwest UX 2017
AI and Machine Learning Demystified by Carol Smith at Midwest UX 2017
 

Similar to xUnit Test Patterns - Chapter19

Tokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテストTokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテスト
Yohei Sato
 
究極のバッチフレームワーク(予定)
究極のバッチフレームワーク(予定)究極のバッチフレームワーク(予定)
究極のバッチフレームワーク(予定)
fumoto kazuhiro
 
Effective java 勉強会
Effective java 勉強会Effective java 勉強会
Effective java 勉強会
Takinami Kei
 
About func unit
About func unitAbout func unit
About func unit
gray Space
 
Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発
Hidekazu Nakamura
 
Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発
Hidekazu Nakamura
 
Unit testで定時帰宅!
Unit testで定時帰宅!Unit testで定時帰宅!
Unit testで定時帰宅!
Funato Takashi
 
リファクタリング読書会20120220
リファクタリング読書会20120220リファクタリング読書会20120220
リファクタリング読書会20120220
Suguru Shirai
 
ありえるえりあ勉強会@五反田~テスト編~ Part3
ありえるえりあ勉強会@五反田~テスト編~ Part3ありえるえりあ勉強会@五反田~テスト編~ Part3
ありえるえりあ勉強会@五反田~テスト編~ Part3
Tomoyuki Sato
 

Similar to xUnit Test Patterns - Chapter19 (19)

Tokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテストTokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテスト
 
究極のバッチフレームワーク(予定)
究極のバッチフレームワーク(予定)究極のバッチフレームワーク(予定)
究極のバッチフレームワーク(予定)
 
Unit test in android
Unit test in androidUnit test in android
Unit test in android
 
tdd4cpp demo
tdd4cpp demotdd4cpp demo
tdd4cpp demo
 
Effective java 勉強会
Effective java 勉強会Effective java 勉強会
Effective java 勉強会
 
ソフトウェア工学2023 11 テスト
ソフトウェア工学2023 11 テストソフトウェア工学2023 11 テスト
ソフトウェア工学2023 11 テスト
 
About func unit
About func unitAbout func unit
About func unit
 
pecl-AOPの紹介
pecl-AOPの紹介pecl-AOPの紹介
pecl-AOPの紹介
 
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
 
Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発
 
Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発
 
Angular2 rc.1 unit testing overview
Angular2 rc.1 unit testing overviewAngular2 rc.1 unit testing overview
Angular2 rc.1 unit testing overview
 
Swift 2.0 で変わったところ「前編」 #cswift
Swift 2.0 で変わったところ「前編」 #cswiftSwift 2.0 で変わったところ「前編」 #cswift
Swift 2.0 で変わったところ「前編」 #cswift
 
CruiseControl.NET設置
CruiseControl.NET設置CruiseControl.NET設置
CruiseControl.NET設置
 
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
 
Unit testで定時帰宅!
Unit testで定時帰宅!Unit testで定時帰宅!
Unit testで定時帰宅!
 
リファクタリング読書会20120220
リファクタリング読書会20120220リファクタリング読書会20120220
リファクタリング読書会20120220
 
TypeScript 言語処理系ことはじめ
TypeScript 言語処理系ことはじめTypeScript 言語処理系ことはじめ
TypeScript 言語処理系ことはじめ
 
ありえるえりあ勉強会@五反田~テスト編~ Part3
ありえるえりあ勉強会@五反田~テスト編~ Part3ありえるえりあ勉強会@五反田~テスト編~ Part3
ありえるえりあ勉強会@五反田~テスト編~ Part3
 

More from Takuto Wada

More from Takuto Wada (20)

組織にテストを書く文化を根付かせる戦略と戦術
組織にテストを書く文化を根付かせる戦略と戦術組織にテストを書く文化を根付かせる戦略と戦術
組織にテストを書く文化を根付かせる戦略と戦術
 
OSS活動の活発さと評価の関係について
OSS活動の活発さと評価の関係についてOSS活動の活発さと評価の関係について
OSS活動の活発さと評価の関係について
 
unassert - encourage reliable programming by writing assertions in production
unassert - encourage reliable programming by writing assertions in productionunassert - encourage reliable programming by writing assertions in production
unassert - encourage reliable programming by writing assertions in production
 
OSS についてあれこれ
OSS についてあれこれOSS についてあれこれ
OSS についてあれこれ
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
 
power-assert, mechanism and philosophy
power-assert, mechanism and philosophypower-assert, mechanism and philosophy
power-assert, mechanism and philosophy
 
アジャイルサムライの次に読む技術書
アジャイルサムライの次に読む技術書アジャイルサムライの次に読む技術書
アジャイルサムライの次に読む技術書
 
Test Yourself - テストを書くと何がどう変わるか
Test Yourself - テストを書くと何がどう変わるかTest Yourself - テストを書くと何がどう変わるか
Test Yourself - テストを書くと何がどう変わるか
 
テスト用ライブラリ power-assert
テスト用ライブラリ power-assertテスト用ライブラリ power-assert
テスト用ライブラリ power-assert
 
Reviewing RESTful Web Apps
Reviewing RESTful Web AppsReviewing RESTful Web Apps
Reviewing RESTful Web Apps
 
power-assert in JavaScript
power-assert in JavaScriptpower-assert in JavaScript
power-assert in JavaScript
 
TDD のこころ @ OSH2014
TDD のこころ @ OSH2014TDD のこころ @ OSH2014
TDD のこころ @ OSH2014
 
テストを書く文化を育てる戦略と戦術
テストを書く文化を育てる戦略と戦術テストを書く文化を育てる戦略と戦術
テストを書く文化を育てる戦略と戦術
 
私にとってのテスト
私にとってのテスト私にとってのテスト
私にとってのテスト
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴
SQLアンチパターン - 開発者を待ち受ける25の落とし穴SQLアンチパターン - 開発者を待ち受ける25の落とし穴
SQLアンチパターン - 開発者を待ち受ける25の落とし穴
 
愛せないコードを書くには人生はあまりにも短い
愛せないコードを書くには人生はあまりにも短い愛せないコードを書くには人生はあまりにも短い
愛せないコードを書くには人生はあまりにも短い
 
ペアプログラミング ホントのところ
ペアプログラミング ホントのところペアプログラミング ホントのところ
ペアプログラミング ホントのところ
 
RESTful Web アプリの設計レビューの話
RESTful Web アプリの設計レビューの話RESTful Web アプリの設計レビューの話
RESTful Web アプリの設計レビューの話
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 

Recently uploaded

Recently uploaded (7)

LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 

xUnit Test Patterns - Chapter19

  • 2. Test Definition ● Test Method – Four-Phase Test ● Assertion Method – Assertion Message ● Testcase Class ● Test Execution ● Test Runner ● Testcase Object ● Test Suite Object ● Test Discovery ● Test Enumeration ● Test Selection
  • 4. How It Works(1) ● テストコードってどこに書くの? ● ひとつひとつのテスト毎にメソッド (Test Method) にし てクラスに配置しましょう ● How It Works ● 各テストをメソッド/手続き/関数のかたちで Four- Phase(358) の実装を行い、Fully Automated Test と する。 ● 大事なのは、 assertion を書いて自己テストコード (Self-Checking Test:26) とすること
  • 5. How It Works(2) ● Test Method には標準 Template がある ● Simple Success Test – 正常系のテスト。 Fixture setup から result verification ま で一本道 ● Expected Exception Test – 例外系のテスト ● Constructor Test – オブジェクトを作成し属性をテストするだけのテスト
  • 6. Why We Do This ● 手続き型言語の場合 ● Test Method をファイルやモジュールに書く ● オブジェクト指向言語の場合 ● Test Method を Testcase Class(373) の中に書 き、Test Discovery(393) や Test Enumeration(399) を使って Test Method を Testcase Object(382) とし てインスタンス化する ● 標準 template に従うことでテストを読みやすくシンプ ルにし、 SUT の動くドキュメントとすることができる
  • 7. Implementation Notes ● どう仕組みを実装する? ● Static method として実装し呼び出しを列挙する – テスト結果を集めたりする共通化がやりにくい ● Test Method 一つ一つに対応する Test Suite Object(387)をつくる – Test Discovery や Test Enumaration でインスタンス化する 場合に便利 ● 静的型付け言語の場合はメソッドに “throws Exception” などを書かなければならない – コンパイラに対して「この例外は Test Runner が処理するよ」 という意思表示になる ● 殆どの Test Method は3パターンに分類できる
  • 8. Simple Success Test ● ソフトウェアには正常系 “happy path” があ る。Simple Success Test はそれを書く ● SUT をインスタンス化して叩き、結果を assert – 言い換えると、 Four-Phase に則ったテストを書く ● 例外はキャッチせず、 Test Automation Framework ま で貫通させる – テストの中で例外を扱うと Obscure Test や誤解のもと – Tests as Documentation の原則を思い出そう – Try-catch を書かない利点は他にもあって、 Test Automation Framework が例外発生行を特定しやすくなる こと
  • 9. Simple Success Test のダメな例 public void testFlightMileage_asKm() throws Exception { //set up fixture Flight newFlight = new Flight(validFlightNumber); try { //exercise SUT newFlight.setMileage(1122);     //verify results int actualKilometres = newFlight.getMileageAsKm(); int expectedKilometres = 1810; //verify results assertEquals( expectedKilometres, actualKilometres); } catch (InvalidArgumentException e) { fail(e.getMessage()); } catch (ArrayStoreException e) { fail(e.getMessage()); } } 不要な try/catch
  • 10. Simple Success Test の良い例 public void testFlightMileage_asKm() throws Exception { //set up fixture Flight newFlight = new Flight(validFlightNumber); newFlight.setMileage(1122); //exercise mileage translator int actualKilometres = newFlight.getMileageAsKm();     //verify results int expectedKilometres = 1810; assertEquals( expectedKilometres, actualKilometres); } xUnit は unexpected exception を 失敗として扱えるので、 throws Exception しておけばよい
  • 11. Expected Exception Test (1) ● 多くの不具合は正常系以外のパスに潜む。特に例 外系のシナリオ。それは、 ● Untested Requirements (268) や、 ● Untested Code (268) であったりするため ● Expected Exception Test はわざと SUT が例外 を出すようなテストを書き、きちんと例外が出ること を調べる ● 例外の中身も調べたいときは Equality Assertion で調 べる ● 例外が出なかったときは fail メソッドなどでテストを失 敗させる
  • 12. Expected Exception Test (2) ● 想定される (expected) 例外には、テストを書いた ほうがよい ● 再現が難しいが、出るかもしれない (might raise) 例外には、テストを書かなくてよい ● (★ ネットワーク障害とか、 Disk full とか) ● そういう例外は Simple Success Test の失敗として現 れるため ● もしそういう例外もテストしたいなら、 Test Stub から例 外を発生させてテストする
  • 13. Expected Exception Test (3) ● 例外をテストするときの仕組み ● JUnit 3.x – ExpectedException クラスを継承させる (?) ● 小さいテストクラスが沢山できるし、あまり旨味は無い ● JUnit 4.x, NUnit – Test Method の annotation/attribute に書く ● Block のある言語 (Smalltalk, Ruby, …) – Block で例外が発生するか調べるテストを書ける
  • 14. Expected Exception Test のダメな例 public void testSetMileage_invalidInput() throws Exception { //set up fixture Flight newFlight = new Flight(validFlightNumber); //exercise SUT     newFlight.setMileage(-1122); //invalid //how do we verify an exception was thrown? } 想定された例外なのにテストが失敗してしまう
  • 15. Expected Exception Test の良い例 public void testSetMileage_invalidInput()throws Exception { //set up fixture Flight newFlight = new Flight(validFlightNumber); try {     //exercise SUT newFlight.setMileage(-1122); fail("Should have thrown InvalidInputException"); } catch(InvalidArgumentException e) { //verify results assertEquals( "Flight mileage must be positive", e.getMessage()); } } 想定される例外を catch する 例外が出なかった場合は fail させる
  • 16. Expected Exception Test の特殊例? public void testSetMileage_invalidInput2() throws Exception { //set up fixture Flight newFlight = new Flight(validFlightNumber); try { //exercise SUT newFlight.setMileage(-1122);     //cannot fail() here if SUT throws same kind of exception } catch(AssertionFailedError e) { //verify results assertEquals( "Flight mileage must be positive", e.getMessage()); return; } fail("Should have thrown InvalidInputException"); } Fail メソッドが投げる例外と同じ例外を SUT が投げる場合にはこう書くしかない !?
  • 17. Method Attribute を使った EET [Test] [ExpectedException(typeof( InvalidArgumentException), "Flight mileage must be > zero")] public void testSetMileage_invalidInput_AttributeWithMessage() { //set up fixture Flight newFlight = new Flight(validFlightNumber);     //exercise SUT newFlight.setMileage(-1122); }
  • 18. Block を使った EET Smalltalk: testSetMileageWithInvalidInput self should: [Flight new mileage: -1122] raise: RuntimeError new 'Should have raised error' Ruby: def testSetMileage_invalidInput flight = Flight.new() assert_raises( RuntimeError, "Should have raised error") do flight.setMileage(-1122) end end
  • 19. Rspec でやってみる describe Flight do before do @flight = Flight.new end it "Should have raised error" do lambda { flight.setMileage(-1122) }.should_raise(RuntimeError) end end
  • 20. Constructor Test ● Fixture Setup phase で作成されたオブジェクトが 正しく作成されているかを各 Test Method で調べ ていると Test Code Duplication (213) がひどく なる ● オブジェクト作成のテストだけ別の Test Method にす ることで他のテストをシンプルにすることができる ● Defect Localication にもなる – 各属性の Test Method を分けるとさらに Defect Localization ● Constructor Test は Simple Success Test の形をと る場合もあるし、 Expected Exception Test の形をと る場合もある
  • 21. Dependency Initialization Test ● Constructor Test の亜種 ● 置き換え可能な依存があるオブジェクトがある場 合、本番環境では本物の依存オブジェクトが参照さ れることをテストする ● ふつうの Constructor Test と分けて管理した方 がよい
  • 22. Constructor Test のダメな例 public void testFlightMileage_asKm2() throws Exception { //set up fixture //exercise constructor Flight newFlight = new Flight(validFlightNumber); //verify constructed object assertEquals(validFlightNumber, newFlight.number); assertEquals("", newFlight.airlineCode); assertNull(newFlight.airline); //set up mileage     newFlight.setMileage(1122); //exercise mileage translator int actualKilometres = newFlight.getMileageAsKm(); //verify results int expectedKilometres = 1810; assertEquals( expectedKilometres, actualKilometres); //now try it with a canceled flight newFlight.cancel(); try { newFlight.getMileageAsKm(); fail("Expected exception"); } catch (InvalidRequestException e) { assertEquals( "Cannot get cancelled flight mileage", e.getMessage()); } } いろいろやりすぎでピントがあっていない Eager Test
  • 23. Constructor Test の良い例1 正常系 public void testFlightConstructor_OK() throws Exception { //set up fixture //exercise SUT Flight newFlight = new Flight(validFlightNumber); //verify results assertEquals(    validFlightNumber, newFlight.number ); assertEquals( "", newFlight.airlineCode ); assertNull( newFlight.airline ); }
  • 24. Constructor Test の良い例2 異常系 public void testFlightConstructor_badInput() { //set up fixture BigDecimal invalidFlightNumber = new BigDecimal(-1023); //exercise SUT try { Flight newFlight = new Flight(invalidFlightNumber);     fail("Didn't catch negative flight number!"); } catch (InvalidArgumentException e) { //verify results assertEquals( "Flight numbers must be positive", e.getMessage()); } }
  • 25. Constructor Test があると他のテストの ピントがはっきりする public void testFlightMileage_asKm() throws Exception { //set up fixture Flight newFlight = new Flight(validFlightNumber); newFlight.setMileage(1122);     //exercise mileage translator int actualKilometres = newFlight.getMileageAsKm(); //verify results int expectedKilometres = 1810; assertEquals( expectedKilometres, actualKilometres); } インスタンス化直後の assertion は不要
  • 27. Test Definition ● Test Method – Four-Phase Test ● Assertion Method – Assertion Message ● Testcase Class ● Test Execution ● Test Runner ● Testcase Object ● Test Suite Object ● Test Discovery ● Test Enumeration ● Test Selection
  • 28. Four-Phase Test ● 良いテストには4つの Phase がある ● Setup ● Exercise ● Verify ● Teardown ● ワンパターン = テストの読みやすさ = Tests as Documentation ● Test Method の中身はテスト内容に集中すべし
  • 29. Four-Phase Test ● どう setup/teardown する? ● Testcase Class per Class または Testcase Class per Feature の場合 – In-line Setup – Garbase-Collected Teardown または In-line Teardown ● Testcase per Fixture の場合 – Implicit Setup ● 例えば setUp メソッド – Implicit Teardown ● 例えば tearDown メソッド
  • 30. 例: Four-Phase Test (In-line) public void testGetFlightsByOriginAirport_NoFlights_inline() throws Exception { //Fixture setup NonTxFlightMngtFacade facade =new NonTxFlightMngtFacade(); BigDecimal airportId = facade.createTestAirport("1OF"); try { //Exercise system List flightsAtDestination1 = facade.getFlightsByOriginAirport(airportId); //Verify outcome assertEquals( 0, flightsAtDestination1.size() ); } finally { //Fixture teardown facade.removeAirport(airportId ); } }
  • 31. 例: 4PT (Implicit Setup/Teardown) NonTxFlightMngtFacade facade = new NonTxFlightMngtFacade(); private BigDecimal airportId; protected void setUp() throws Exception { //Fixture setup super.setUp(); airportId = facade.createTestAirport("1OF"); } public void testGetFlightsByOriginAirport_NoFlights_implicit() throws Exception { //Exercise SUT List flightsAtDestination1 = facade.getFlightsByOriginAirport(airportId); //Verify outcome assertEquals( 0, flightsAtDestination1.size() ); } protected void tearDown() throws Exception { //Fixture teardown facade.removeAirport(airportId); super.tearDown(); }
  • 33. Test Definition ● Test Method – Four-Phase Test ● Assertion Method – Assertion Message ● Testcase Class ● Test Execution ● Test Runner ● Testcase Object ● Test Suite Object ● Test Discovery ● Test Enumeration ● Test Selection
  • 34. Assertion Method ● どうやってテストに自己チェックさせるか ● ユーティリティメソッドを呼ぶことで望んだ結果になった かどうかの評価をすればいい ● Fully Automated Tests(26)の肝は、テストをSelf- Checking Tests(26)にすること ● そのためには期待する結果を表現し、自動でチェックす ることが必要 ● Assertion Method は期待する結果を表現し、 ● コンピュータにとっては実行可能に ● 人間には Tests as Documentation(23) になる。
  • 35. Why We Do This ● 期待する結果を Conditional Test Logic(200)で 表現すると… ● 饒舌に過ぎ、読むのも理解するのも難しい ● Test Code Duplication(213)が発生しやすい ● Buggy Test(260)も発生しやすい ● Assetion Method はこの問題を… ● 再利用性の高い Test Utility Methods(599)に複雑さ を移すことにより解決する ● そのメソッドの正しさは Test Utility Tests(599)でテス トすることも可能
  • 36. まずはひどい例から if (x.equals(y)) { throw new AssertionFailedError( "expected: <" + x.toString() + "> but found: <" + y.toString() + ">"); } else { // Okay, continue // ... } // 上の例では NPE が発生するのでガード節を入れてみると…     if (x == null) { //cannot do null.equals(null) if (y == null ) { //they are both null so equal return; } else { throw new AssertionFailedError( "expected null but found: <" + y.toString() +">"); } } else if (!x.equals(y)) { //comparable but not equal! throw new AssertionFailedError( "expected: <" + x.toString() + "> but found: <" + y.toString() + ">"); } //equal
  • 37. JUnit はこうリファクタリングした /** * Asserts that two objects are equal. If they are not, * an AssertionFailedError is thrown with the given message. */ static public void assertEquals(String message, Object expected, Object actual) { if (expected == null &&actual == null) return;     if (expected != null && expected.equals(actual)) return; failNotEquals(message, expected, actual); } --------------------------------- assertEquals( x, y ); // 呼び出し側はこれだけ!!
  • 38. Implementation Notes ● 全ての xUnit ファミリーは Assetion Method を備 えているが、考えるべきことはある ● Assertion Method をどうやって呼ぶか ● 最適な Assertion Method をどう選ぶか ● Assertion Message(370) に何を書くか
  • 39. Calling Built-in Assertion Methods ● Test Method(348) からテストフレームワーク組み 込みで提供されている Assertion Method を呼ぶ には… ● フレームワークが提供する Testcase Superclass(638) を継承する (JUnit タイプ) ● グローバルクラス/モジュールを完全修飾名で呼び出す (NUnit タイプ) ● Mixin (Test::Unit タイプ) ● マクロ (CppUnit タイプ)
  • 40. Assertion Messages ● テスト失敗時の出力に含めるメッセージ ● どのテストが失敗したかをわかりやすくする – Assertion Roulette 参照 ● 省略可能な引数として Assertion Method に渡すかた ちが多い ● テスト失敗時に「なぜ失敗したか」の情報が多けれ ばデバッグは容易になる ● 正しい Assertion Method を選ぶことはエラー時のメッ セージ出力を適切にする意味でも重要 ● 問題なのは、 Assertion Message の引数の順番 が xUnit 毎にブレていること
  • 41. Choosing the Right Assertion ● Assertion Method には二つのゴールがある ● 期待しない結果のときにはテストを失敗させること ● SUT がどう振る舞うかのドキュメントになること ● これらのゴールを満たすため、最適な Assertion Method を選ぶことが重要になる ● Assertion Method には以下のカテゴリがある ● Single-Outcome Assertions ● Stated Outcome Assertions ● Expected Exception Assertions ● Equality Assertions ● Fuzzy Equality Assertions
  • 42. Equality Assertion ● 結果が期待値と等価かどうかを調べる ● もっとも使われる Assertion Method ● 引数の順番は規約としては expected, actual の 順番 ● 順番は失敗時のメッセージに関係するので重要 ● ★この順番でない xUnit もある。(NUnit とか) ● ★自分の使う Equality Assertion の順を覚えよう ● 内部では等価性を調べるメソッドが呼ばれる ● Java では equals とか ● SUT ごと調べたい場合は Test-Specific Subclass
  • 43. Equality Assertion いろいろ assertEquals( expected, actual ); // since JUnit3.x assertThat( actual, is(expected) ); // since JUnit 4.4 Assert.AreEqual( actual, expected ); // NUnit is( actual, expected ); // Test::Simple (Perl) equals( actual,     expected ); // QUnit actual.should == expected // Rspec
  • 44. Fuzzy Equality Assertion ● 結果と期待値との完全な一致が難しい場合 ● 浮動小数点をあつかうとき ● 期待値と完全一致させるには結果に本質的でない不要 なゴミが多いとき (XML の空白ノードとか) assertEquals( 3.1415, diameter/2/radius, 0.001); assertEquals( expectedXml, actualXml, elementsToCompare );
  • 45. Stated Outcome Assertion ● 期待値を渡す必要がないとき ● Conditional Test Logic を避けるためのガード節 としても使える assertNotNull(a ); assertTrue(b > c ); assertNonZero(b );
  • 46. Expected Exception Assertion ● ブロックやクロージャを備えている言語は発生する であろう例外をパラメータとして渡す Assertion Method が使える self should: [Flight new mileage: -1122] raise: RuntimeError new 'Should have raised error' assert_raises( RuntimeError, "Should have raised error") { flight.setMileage(-1122) } assert_raises( RuntimeError, "Should have raised error")do flight.setMileage(-1122) end
  • 47. Single-Outcome Assertion ● 常に同じ振る舞いをする Assertion Method ● 例えば fail メソッド ● 使われる状況 ● まだ完成していないテスト Unfinished Test Assertion を示す ● Expected Exception Test の中の try/catch ブロックで使う fail( "Expected an exception" ); unfinishedTest();
  • 48. Single-Outcome Assertion の例 public void testSetMileage_invalidInput()throws Exception { //set up fixture Flight newFlight = new Flight(validFlightNumber); try {     //exercise SUT newFlight.setMileage(-1122); fail("Should have thrown InvalidInputException"); } catch(InvalidArgumentException e) { //verify results assertEquals( "Flight mileage must be positive", e.getMessage()); } } 想定される例外を catch する 例外が出なかった場合は fail させる
  • 50. Test Definition ● Test Method – Four-Phase Test ● Assertion Method – Assertion Message ● Testcase Class ● Test Execution ● Test Runner ● Testcase Object ● Test Suite Object ● Test Discovery ● Test Enumeration ● Test Selection
  • 51. Assertion Message ● どの Assertion Method が落ちたか知りたい ● Assertion Method 毎にメッセージ引数を渡す ● テスト失敗時の出力に含めるメッセージ ● どのテストが失敗したかをわかりやすくする – Assertion Roulette 参照 ● 省略可能な引数として Assertion Method に渡すかた ちが多い
  • 52. When to Use It ● 二つの学派(School)がある ● テスト駆動派 (Test drivers) と、その他派 ● テスト駆動派 ● “single assertion per Test Method” ● Test Method に Assertion Method がひとつしかな いので、どの Assertion Method が落ちたかは自明。 故に Assertion Message は不要。 ● その他派 ● ひとつの Test Method に複数 Assertion Method が あるので、 Assertion Message を使いたくなる
  • 53. Implementation Notes ● Assertion Message に何を書くべきか ● Assertion-Identifying Message ● Expectation-Describing Message ● Argument-Describing Message
  • 54. Assertion-Identifying Message ● 同種の Assertion Method が複数ある場合にどれ が失敗したか分かりにくい ● Assertion Method 毎に違う文字列を渡して識別する ● 識別に使う文字列の例 ● Assertion Method に使う変数名 – 名前に悩む必要がないので便利かも ● 単なる連番 – テストコードを読まないと結局どこが失敗したかわからなかっ たりする
  • 55. Expectation-Describing Message ● テストが失敗した時に「実際何が起こったか」はわ かる。だが「何が起こるべきだったか」はわからな い。 ● テストコード内にコメントを書く手もある ● もっと良いのは Assertion Message に期待値の 説明を書くこと ● Equality Assertion の場合は自動でやってくれるので 必要無し ● Stated Outcome Assertion の場合は入れなければ わからない
  • 56. Argument-Describing Message ● いくつかの Assertion Method は失敗時の出力が 不親切 ● 特に Stated Outcome Assertion – assertTrue(式) とか – 失敗したのはわかるが、どんな式が失敗したのかわからない – 式を Assertion Message に含めてしまう手がある
  • 57. Argument-Describing Messageの例 assertTrue( "Expected a > b but a was '" + a.toString() + "' and b was '" + b.toString() + "'", a.gt(b) ); assertTrue( "Expected b > c but b was '" + b.toString() + "' and    '" + c.toString + "'", b > c ); } c was