Repository navigation
Expand file tree
/
Copy pathchapter21_delta_assertion.txt
More file actions
146 lines (121 loc) · 6.75 KB
/
Copy pathchapter21_delta_assertion.txt
File metadata and controls
146 lines (121 loc) · 6.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
{{toc}}
! Delta Assertion
*フィクスチャの初期状態は操作できない環境でセルフチェックテストを実行する
*exerciseの前後における差異を元にしたアサーションを作成する
*Delta Assertion
**Shared Fixture に存在するデータに対する依存性を軽減する
*テストデータベースとして shared fixture (317ページ) を使っている
**SUTのexercise後のfixtureのコンテンツ状態に依存したアサーションを書くのは大変
**他のテストでfixtureに作られたオブジェクトがアサーションを失敗させてしまうかも
*Database Partitioning Scheme (Database Sandbox, 650ページ) を使えば他のテストから独立できる
**その選択肢が選べないときはどうする?
!! How it Works
* exercise の前にやること
**Shared Fixture の適当なスナップショットを取得する
* exercise の後にやること
**取得したスナップショットに相対的なアサーションを定義する
*Delta Assertion の例
**変更されたオブジェクトの数は正しいか
**SUTから返されるオブジェクトの集合のコンテンツは期待通りに増えているか
!! When Use It
*使いどき
**test fixture のコントロールが無い
**Interacting Test(Erratic Test, 228ページ) を無くしたい
**同じテストランナーで複数のテストを実行する
**SUTとフィクスチャを変更するのが私たちのテストだけである
*メリット
**テストコードに、フィクスチャに対する柔軟性ができる
**メモリ/データリークの検出ができる
***Implicit Teardown(516ページ)と合わせて使う
***詳しくは 487 ページのコラムを参照
*できないこと
**複数プロセスが並行して実行するような Test Run War は防げない
!! Implementation Notes
*exercise前にできることは 1 つ
**スナップショットを取得した後で、SUT がスナップショットの状態を変更できないようにする
*例:スナップショットがオブジェクトの集合のとき
**ディープコピー
**シャローコピーでは、コレクションが参照してるオブジェクトをSUTから変更できてしまう
*exercise後にできることは複数
**何もしない
***(1)個数を数えて、(2)スナップショットに含まれてるオブジェクトが存在することを確認し、(3)新たなExpected Object(462ページ)が存在することを確認する
**スナップショットに含まれるものは全部削除
***新たなExpected Objectsが存在することを確認する
**実現方法
***Custom Assertion(474ページ)
***Verification Method
!! Using Delta Assertions to Detect Data Leakage
* カスタマーテスト実行後のテストフィクスチャの初期化方法について実験していた
* 私達のテストはデータベースにアクセスして、いくつかのオブジェクトを残していた
* このとき問題になっていたこと
** Unrepeatable Test
** Interacting Test
** Slow Test
* Automated Tear down で、テスト中に作られたオブジェクトを記録するようにした
** データベースをスタブ化できることが分かった (Fake Database (551ページ))
* データベースがフェイクでもリアルでも同じテストを実行できるようにした
** 相互作用に起因する問題は解決した
** テストはオブジェクトを残し続けていた
** どのテストが悪いのかつきとめる必要がある
* 解決方法はシンプル
** フェイクデータベースの中を見た
*** 実装はハッシュテーブルにオブジェクト数を返すメソッドを追加したもの
* 全てのオブジェクトを初期化できていることを検証した
** setup メソッドでデータベースのオブジェクト数を取得してインスタンス変数に記録
** 記録したオブジェクト数を Equality Assertion の期待値とする
* どのテストメソッドがデータをリークしているかすぐに分かった
* 私達の気付き
** 同じテストを繰り返し実行できることの有用性
** データベースをインメモリで持つことの有用性
* 社内のテストケーススーパークラスを使ってたら teardown で落ちた
** Delta Assertion を実装してる
** メモリリークの検出が同じように出来た
!! Motivating Example
Shared Fixture を使っているため、他のテストを実行すると失敗してしまうかもしれないテスト。
// public void testGetFlightsByOriginAirport_OneOutboundFlight()
// throws Exception {
// FlightDto expectedFlightDto =
// createNewFlightBetweenexistingAirports();
// // Exercise System
// facade.createFlight(
// expectedFlightDto.getOriginAirportId(),
// expectedFlightDto.getDestinationAirportId());
// // Veirfy Outcome
// List flightsAtOrigin = facade.getFlightsByOriginAirport(
// expectedFlightDto.getOriginAirportId());
// assertOnlyFlightInDtoList( "Outbound flight at origin",
// expectedFlightDto,
// flightsAtOrigin);
// }
!! Refactoring Notes
* Delta Assertion を使うようにテストを変更する
** 後で検証するデータ(もしくはオブジェクト集合) のスナップショットを取得する
** アサーションの内容を、事前データと事後データの差異を検証するものに変更する
* Conditional Test Logic にならないように、カスタムアサーションを導入する
** 事前データを引数に取れるようにしたもの
!!! Example: Delta Assertion
// public void testCrateFlight_Delta()
// throws Exception {
// FlightDto expectedFlightDto =
// createNewFlightBetweenexistingAirports();
// // Remember prior state
// List flightsBeforeCreate =
// facade.getFlightsByOriginAirport(
// expectedFlightDto.getOriginAirportId());
// // Exercise system
// facade.createFlight(
// expectedFlightDto.getOriginAirportId(),
// expectedFlightDto.getDestinationAirportId());
// // Verify outcome relative to prior state
// List flightsAfterCreate =
// fasace.getFlightsByOriginAirport(
// expectedFlightDto.getOriginAirportId());
// assertFlightIncludedInDtoList( "new flight ",
// expectedFlightDto,
// flightsAfterCreate);
// assertAllFlightsIncludedInDtoList( "previous flights",
// flightsBeforeCreate,
// flightsAfterCreate);
// assertEquals( "Number of flights after create",
// flightsBeforeCreate.size()+1,
// flightsAfterCreate.size());