Repository navigation
Expand file tree
/
Copy pathchapter21_custom_assertion.txt
More file actions
667 lines (582 loc) · 33.9 KB
/
Copy pathchapter21_custom_assertion.txt
File metadata and controls
667 lines (582 loc) · 33.9 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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
! Custom Assertion
How do we make tests self-checking when we have test-specific equality logic?
How do we reduce Test Code Duplication when the same assertion
logic appears in many tests?
How do we avoid Conditional Test Logic?
We create a purpose-built Assertion Method that compares only those
attributes of the object that define test-specific equality.
* テストに固有の等価性判定ロジックがある場合、セルフチェックテストを構築するにはどうすればよいか ?
* 同一のアサーションロジックが複数のテストに使われている場合、テストコードの重複を避けるにはどうすればよいか ?
* 条件依存のテストロジックを無くすにはどうすればよいか ?
* テストに固有の等価性判定で使われている属性だけを比較するような、目的に合わせたアサーションメソッドを作成する。
Most members of the xUnit family provide a reasonably rich set of Assertion
Methods (page 362). But sooner or later, we inevitably find ourselves saying,
“This test would be so much easier to write if I just had an assertion that did . . . .”
So why not write it ourselves?
The reasons for writing a Custom Assertion are many, but the technique is
pretty much the same regardless of our goal. We hide the complexity of whatever
it takes to prove the system is behaving correctly behind an Assertion Method
with an Intent-Revealing Name [SBPP].
* たいていの xUnit ファミリーにはアサーションメソッドのための適度な道具立てがある
* しかしやがては次のような言葉を口にするでしょう。
** 「某のアサーションがあればこのテストはもっと簡単に書けるのに...」
* 自分達で書いてはどうでしょうか?
* カスタムアサーションを書くにあたっての問題
** その方法は目的に係らずほとんど同じです。
* Intent-Revealing Name (SBPP) によるアサーションメソッド
** システムの検証による複雑さを隠蔽できる
!! How It Works
We encapsulate the mechanics of verifying that something is true (an
assertion) behind an Intent-Revealing Name. To do so, we factor out
all the common assertion code within the tests into a Custom Assertion
that implements the verification logic. A Custom Equality Assertion
takes two parameters: an Expected Object (see State Verification on
page 462) and the actual object. A key characteristic of Custom
Assertions is that they receive everything they need to pass or fail
the test as parameters. Other than causing the test to fail, they have
no side effects.
* Intent-Revealing Name により、真になるアサーションの検証の方法をカプセル化する
* すべてのアサーションから共通するコードを抽出し、カスタムアサーションとして検証ロジックを実装する
* Custom Equality Assertion は 2 つのパラメータを取る
** 期待値オブジェクト (State Verification 462ページ)
** 結果値オブジェクト
* カスタムアサーションの要点
** テスト結果の是非を判断するために必要な情報を、すべてパラメータとして渡すこと
* Other than causing the test to fail, they have no side effects.
** what's the meaning of "Other" ?
Typically, we create Custom Assertions through refactoring by identifying
common patterns of assertions in our tests. When test driving, we might just
go ahead and call a nonexistent Custom Assertion because it makes writing our
test easier; this tactic lets us focus on the part of the SUT that needs to be tested
rather than the mechanics of how the test would be carried out.
* カスタムアサーションは、テストコードのリファクタリングを通じて、共通するパターンを見出すことで作成する
* SUT のテスト対象部分について集中する方法
** テストコードを書くとき、まだ書いてないしないカスタムアサーションを呼び出す
** この方法は、テストが実行された仕組みに依存しない
!! When to Use It
We should consider creating a Custom Assertion whenever any of the following
statements are true:
• We find ourselves writing (or cloning) the same assertion logic in test
after test (Test Code Duplication; see page 213).
• We find ourselves writing Conditional Test Logic (page 200) in the result
verification part of our tests. That is, our calls to Assertion Methods are
embedded in if statements or loops.
• The result verification parts of our tests suffer from Obscure Test
(page 186) because we use procedural rather than declarative result
verification in the tests.
• We find ourselves doing Frequent Debugging (page 248) whenever
assertions fail because they do not provide enough information.
*カスタムアサーションを作成にあたっての条件
** Test Code Duplication (213ぺーじ) が発生している
*** 同じアサーションロジックがあちこちに
** Conditional Test Logic (200ページ) が登場する
*** アサーションメソッドが if 文やループの中に埋め込まれている
** Obscure Test (186ページ) で悩んでいる
*** テストコード中の結果の検証が宣言的ではなく手続的になってる
** Frequent Debugging (248ページ) をよくやっている
*** アサーションが失敗したときに提供される情報が不十分
A key reason for moving the assertion logic out of the tests and into
Custom Assertions is to Minimize Untestable Code (see page 44). Once
the verification logic has been moved into a Custom Assertion, we can
write Custom Assertion Tests (see Custom Assertion on page 474) to
prove the verification logic is working properly. Another important
benefit of using Custom Assertions is that they help avoid Obscure
Tests and make tests Communicate Intent (see page 41). That, in turn,
will help produce robust, easily maintained tests. If the
verification logic must interact with the SUT to determine the actual
outcome, we can use a verification Method (see Custom Assertion)
instead of a Custom Assertion. If the setup and exercise parts of the
tests are also the same except for the values of the actual/expected
objects, we should consider using a Parameterized Test (page 607). The
primary advantage of Custom Assertions over both of these techniques
is reusability; the same Custom Assertion can be reused in many
different circumstances because it is independent of its context (its
only contact with the outside world occurs through its parameter
list). We most commonly write Custom Assertions that are Equality
Assertions (see Assertion Method), but there is no reason why we
cannot write other kinds as well.
*テストコードからアサーションロジックを抽出してカスタムアサーションに移動する目的
** Minimize Untestable Code (44ページ) のため
**カスタムアサーションのテストを書けるようになる
*** 検証ロジックが正しく動作することを証明できる
*カスタムアサーションを使うことのメリット
**再利用性
*** コンテキストに依存しないから、異なる環境でも再利用しやすい
**Obscure Test を回避する
**テストを Communicate Intent (41ページ) にする
**プロダクトを頑健にする
**テストのメンテナンスを容易にする
* Verification Method (後述) をお勧めするケース
** 検証ロジックが、本当の出力を得るために SUT とやりとりしなければならない
* Parameterized Test (607ページ) をお勧めするケース
** setup や exercise の部分で SUT とやりとりしなければならない
*カスタムアサーションの再利用性
** コンテキストに非依存
** 外界とのインターフェースはパラメータリストだけ
** 有名なのは Equality Assertions (assertEqual, assertThat, etc ...)
!!! Variation: Custom Equality Assertion
For custom equality assertions, the Custom Assertion must be passed an
Expected Object and the actual object to be verified. It should also
take an Assertion Message (page 370) to avoid playing Assertion
Roulette (page 224). Such an assertion is essentially an equals method
implemented as a Foreign Method [Fowler].
!!! Variation: Object Attribute Equality Assertion
We often run across Custom Assertions that take one actual object and
several different Expected Objects that need to be compared with
specific attributes of the actual object. (The set of attributes to be
compared is implied by the name of the Custom Assertion.) The key
difference between these Custom Assertions and a verification Method
is that the latter interacts with the SUT while the Object Attribute
Equality Assertion looks only at the objects passed in as parameters.
* 特定の属性を検証するアサーションメソッド
** メソッド名に検証する属性を示す言葉が含まれている
* verification Method との違い
** Object Attribute Equality Assertion はパラメータのオブジェクトしか見ない
** verification Method では SUT にアクセスする
!!! Variation: Domain Assertion
All of the built-in Assertion Methods are domain independent. Custom
Equality Assertions implement test-specific equality but still compare
only two objects. Another style of Custom Assertion helps contribute
to the definition of a “domain-specific” Higher-Level Language (see
page 41)—namely, the Domain Assertion.
A Domain Assertion is a Stated Outcome Assertion (see Assertion
Method) that states something that should be true in domain-specific
terms. It helps elevate the test into “business-speak.”
* 全ての組み込みアサーションメソッドはドメイン非依存
* Custom Equality Assertion は、テストに限定された 2 つのオブジェクト間の等価性を実装するだけ
* Domain Assertion
** ドメインに限定された Higher-Level Language (41ページ) を定義する
** Stated Outcome Assertion (Assertion Method 参照)
** ドメインで規程された条件において真となるアサーション
** テストケースをビジネス用語で表現
!!! Variation: Diagnostic Assertion
Sometimes we find ourselves doing Frequent Debugging whenever a test
fails because the assertions tell us only that something is wrong but
do not identify the specific problem (e.g., the assertions indicate
these two objects are not equal but it isn’t clear what isn’t equal
about the object). We can write a special kind of Custom Assertion
that may look just like one of the built-in assertions but provide
more information about what is different between the expected and
actual values than a built-in assertion because it is specific to our
types. (For example, it might tell us which attributes are different
or where long strings differ.)
* アサーションが失敗
** 失敗したことは分かるけど...
** 具体的に何が駄目なのか特定できないので Frequent Debugging
* 組み込みアサーションと同じような使い勝手だけど、失敗の原因を詳しく出力するアサーション
On one project, we were comparing string variables containing XML.
Whenever a test failed, we had to bring up two string inspectors and scroll
through them looking for the difference. Finally, we got smart and included
the logic in a Custom Assertion that told us where the first difference between
the two XML strings occurred. The small amount of time we spent writing
the diagnostic custom assertion was paid back many times over as we ran
our tests.
* XML文字列を比較するテストが失敗
** インスペクタで文字列をスクロールしながら確認...
* 異なる最初の部分を出力できるカスタムアサーションを導入した
** ちょっと時間はかかったけど、充分ペイするだけの価値はあった
!!! Variation: verification Method
In customer tests, a lot of the complexity of verifying the outcome is
related to interacting with the SUT. verification Methods are a form
of Custom Assertions that interact directly with the SUT, thereby
relieving their callers from this task. This simplifies the tests
significantly and leads to a more “declarative” style of outcome
specification. After the Custom Assertion has been written, we can
write subsequent tests that result in the same outcome much more
quickly. In some cases, it may be advantageous to incorporate even
the exercise SUT phase of the test into the verification Method. This
is one step short of a full Parameterized Test that incorporates all
the test logic in a reusable Test Utility Method (page 599).
* カスタマーテストにおける結果検証の複雑さは SUT とのやり取りがあることに起因する
* verification Method
** SUT と直接やりとりするカスタムアサーション
** アサーションを実行する側 (テストコード) で SUT へアクセスしなくてもよくなる
** テストコードが劇的にシンプルに
** 宣言的な仕様を導出
** exercise phase のコードも取り込める
* 完全な Parameterized Test への第一歩
** Test Utility Method (599ページ)
!! Implementation Notes
The Custom Assertion is typically implemented as a set of calls to the various
built-in Assertion Methods. Depending on how we plan to use it in our tests,
we may also want to include the standard Equality Assertion template to ensure
correct behavior with null parameters. Because the Custom Assertion is itself an
Assertion Method, it should not have any side effects, nor should it call the SUT.
(If it needs to do so, it would be a verification Method.)
* カスタムアサーションの実装
** 組み込みアサーション呼び出しの組み合わせ
** NULL パラメータでも正しい振る舞いをすることを保証する
*** 標準的な Equality Assertion の形式を取るようにしたい
*** テストコードでの使い方に関する想定にもよるが...
** 副作用を持つべきではない
*** カスタムアサーションもアサーションメソッドなのだから...
** SUT を呼び出すべきでhaない
*** SUT の呼び出しが必要なら、それは verification Method になる
!!! Variation: Custom Assertion Test
Testing zealots would also write a Custom Assertion Test (a
Self-Checking Test—see page 26—for Custom Assertions) to verify the
Custom Assertion. The benefit from doing so is obvious: increased
confidence in our tests. In most cases, writing Custom Assertion Tests
isn’t particularly difficult because Assertion Methods take all their
arguments as parameters. We can treat the Custom Assertion as the SUT
simply by calling it with various arguments and verifying that it
fails in the right cases. Single-Outcome Assertions (see Assertion
Method) need only a single test because they don’t take any
parameters (other than possibly an Assertion Message). Stated Outcome
Assertions need one test for each possible value (or boundary
value). Equality Assertions need one test that compares two objects
deemed to be equivalent, one test that compares an object with itself,
and one test for each attribute whose inequality should cause the
assertion to fail. Attributes that don’t affect equality can be
verified in one additional test because the Equality Assertion should
not raise an error for any of them.
* テスト狂は、カスタムアサーションをテストするためのカスタムアサーションを書く
* 私たちのテストがより信頼できるものになるというメリット
* カスタムアサーションのテストを書くのは難しい
** すべての引数がパラメータ
* カスタムアサーションを SUT と同じように扱う
** 様々な引数を渡す
** 正しく失敗することを検証する
* Single-Outcome Assertions
** 1 つのテストがあればよい
** アサーションメッセージの他に引数を取らないから
* Stated Outcome Assertions
** 可能な値 (もしくは境界値) ごとに 1 つのテストが必要
* Equality Assertions
** 等価比較をしようとしている 2 つのオブジェクトを比較するテスト
** 2 つのオブジェクトのうち、いずれかのオブジェクトとそれ自身を比較するテスト
** アサーションを失敗させるオブジェクトの属性を比較するテスト
** ついでに等価性に影響しない属性を検証するテストも追加で書いておく
The Custom Assertions follow the normal Simple Success Test (see Test
Method on page 348) and Expected Exception Test (see Test Method)
templates with one minor difference: Because the Assertion Method is
the SUT, the exercise SUT and verify outcome phases of the Four-Phase
Test (page 358) are combined into a single phase.
* 形式の話
** 普通の Simple Success Test の形式 (テストメソッド、348ページ) に従う
** Expected Exception Test の形式 (テストメソッド) に従う
* ちょっとだけ違うところ
** アサーションメソッドが SUT でもあること
** Four-Phase Test (358ページ) が 1 つのフェーズに結合される
Each test consists of setting up the Expected Object and the actual
object and then calling the Custom Assertion. If the objects should be
equivalent, that’s all there is to it. (The Test Automation Framework
described on page 298 would catch any assertion failures and fail the
test.) For the tests where we expect the Custom Assertion to fail, we
can write the test as an Expected Exception Test (except that the
exercise SUT and verify outcome phases of the Four-Phase Test are
combined into the single call to the Custom Assertion).
* テストの成り立ち
** 期待値オブジェクトの準備
** 期待値オブジェクトと結果値オブジェクトでカスタムアサーションを呼び出すこと
** オブジェクトが等価であるならそれしかやることはない
* 自動化テストフレームワーク (298ページ)
** あらゆるアサーションの失敗を catch し テストを失敗させることになってる
* カスタムアサーションが失敗することを期待するようなテストは Expected Exception Test で
** Four-Phase Test が集約されてない場合を除く
The simplest way to build the objects to be compared for a specific
test is to do something similar to One Bad Attribute (see Derived
Value on page 718)—that is, build the first object and make a deep
copy of it. For successful tests, modify any of the attributes that
should not be compared. For each test failure, modify one attribute
that should be grounds for failing the assertion.
* 特定のテストで比較に使われるオブジェクトを簡単に構築する方法
** オブジェクトを deep copy
** One Bad Attribute (Derived Value、718ページ) によく似ているが...
* 成功するテスト
** 比較しない属性を変える
* 失敗させるテスト
** アサーションを失敗させる属性を変える
A brief warning about a possible complication in a few members of the
xUnit family: If all of the test failure handling does not occur in
the Test Runner (page 377), calls to fail or built-in assertions may
add messages to the failure log even if we catch the error or
exception in our Custom Assertion Test. The only way to circumvent
this behavior is to use an “Encapsulated Test Runner” to run each
test by itself and verify that the one test failed with the expected
error message.
*いくつかの xUnit ファミリーについて、複雑な問題になりうることの警告
** Test Runner (377ページ) でエラーハンドリング機構が動かない
** fail や組み込みアサーションはログにメッセージを出力するだけ
*** カスタムアサーションのテストでエラーや例外を catch したくても...
*そんなときのやりかた
** "Encapsulated Test Runner" で Test Runner を駆動
** テストが失敗したときのエラーメッセージを検証する
!! Motivating Example
In the following example, several test methods repeat the same series
of assertions:
// public void testInvoice_addOneLineItem_quantity1_b() {
// // Exercise
// inv.addItemQuantity(product, QUANTITY);
// // Verify
// List lineItems = inv. getLineItems();
// assertEquals("number of items", lineItems. size(), 1);
// // Verify only item
// LineItem expItem = new LineItem(inv, product, QUANTITY) ;
// LineItem actual = (LineItem) lineItems. get(0);
// assertEquals(expItem. getInv(), actual. getInv()) ;
// assertEquals(expItem. getProd(), actual.getProd()) ;
// assertEquals(expItem. getQuantity(), actual. getQuantity()) ;
// }
// public void testRemoveLineItemsForProduct_oneOfTwo() {
// // Setup
// Invoice inv = createAnonInvoice();
// inv.addItemQuantity(product, QUANTITY);
// inv.addItemQuantity(anotherProduct, QUANTITY) ;
// LineItem expItem = new LineItem(inv, product, QUANTITY) ;
// // Exercise
// inv.removeLineItemForProduct(anotherProduct);
// // Verify
// List lineItems = inv. getLineItems();
// assertEquals("number of items", lineItems. size(), 1);
// LineItem actual= (LineItem) lineItems. get(0);
// assertEquals(expItem. getInv(), actual. getInv()) ;
// assertEquals(expItem. getProd(), actual.getProd()) ;
// assertEquals(expItem. getQuantity(), actual. getQuantity()) ;
// }
// //
// // Adding TWO line items
// //
// public void testInvoice_addTwoLineItems_sameProduct() {
// Invoice inv = createAnonInvoice();
// LineItem expItem1 = new LineItem(inv, product, QUANTITY1) ;
// LineItem expItem2 = new LineItem(inv, product, QUANTITY2) ;
// // Exercise
// inv.addItemQuantity(product, QUANTITY1);
// inv.addItemQuantity(product, QUANTITY2);
// // Verify
// List lineItems = inv. getLineItems();
// assertEquals("number of items", lineItems. size(), 2);
// // Verify first item
// LineItem actual= (LineItem) lineItems. get(0);
// assertEquals(expItem1. getInv(), actual.getInv() );
// assertEquals(expItem1. getProd(), actual. getProd() );
// assertEquals(expItem1. getQuantity(), actual. getQuantity() );
// // Verify second item
// actual= (LineItem)lineItems.get(1);
// assertEquals(expItem2. getInv(), actual.getInv() );
// assertEquals(expItem2. getProd(), actual. getProd() );
// assertEquals(expItem2. getQuantity(), actual. getQuantity() );
// }
Note that the first test ends with a series of three assertions and
the second test repeats the series of three assertions twice, once for
each line item. This is clearly a bad case of Test Code Duplication.
最初のテストは 3 つのアサーションで終わっている。
2 つ目のテストは各行について 3 つのアサーションを 2 回繰り返している。
Test Code Duplication です。
!! Refactoring Notes
Refactoring zealots can probably see that the solution is to do an
Extract Method [Fowler] refactoring on these tests. If we pull out all
the common calls to Assertion Methods, we will be left with only the
differences in each test. The extracted method is our Custom
Assertion. We may also need to introduce an Expected Object to hold
all the values that were being passed to the individual Assertion
Methods on a single object to be passed to the Custom Assertion.
* リファクタリング厨:Extract Method(Fowler) を使うことに気付く
** 共通するアサーションメソッドの呼び出しを抽出しても、テストごとの些少な差異に煩わされる
* 全ての値を保持する期待値オブジェクトを導入
** オブジェクトを介して個々のアサーションメソッドに値を提供
** カスタムアサーションには 1 つの引数として期待値オブジェクトを渡す
!!! Example: Custom Assertion
In this test, we use a Custom Assertion to verify that LineItem
matches the expected LineItem(s). For one reason or another, we have
chosen to implement a test-specific equality rather than using a
standard Equality Assertion.
* LineItem を検証するカスタムアサーションを実装
* ある理由により、標準的な Equality Assertion ではなく テスト固有の等値メソッドを実装
// public void testInvoice_addOneLineItem_quantity1_() {
// Invoice inv = createAnonInvoice();
// LineItem expItem = new LineItem(inv, product, QUANTITY) ;
// // Exercise
// inv.addItemQuantity(product, QUANTITY);
// // Verify
// List lineItems = inv. getLineItems();
// assertEquals("number of items", lineItems. size(), 1);
// // Verify only item
// LineItem actual= (LineItem) lineItems. get(0);
// assertLineItemsEqual("LineItem", expItem, actual) ;
// }
// public void testAddItemQuantity_sameProduct_() {
// Invoice inv = createAnonInvoice();
// LineItem expItem1 = new LineItem(inv, product, QUANTITY1) ;
// LineItem expItem2 = new LineItem(inv, product, QUANTITY2) ;
// // Exercise
// inv.addItemQuantity(product, QUANTITY1);
// inv.addItemQuantity(product, QUANTITY2);
// // Verify
// List lineItems = inv. getLineItems();
// assertEquals("number of items", lineItems. size(), 2);
// // Verify first item
// LineItem actual= (LineItem) lineItems. get(0);
// assertLineItemsEqual("Item 1", expItem1,actual);
// // Verify second item
// actual= (LineItem)lineItems.get(1);
// assertLineItemsEqual("Item 2", expItem2, actual) ;
// }
The tests have become significantly smaller and more
intent-revealing. We have also chosen to pass a string indicating
which item we are examining as an argument to the Custom Assertion to
avoid playing Assertion Roulette when a test fails.
* テストコードは目に見えて小さくなった
** 意図も分かりやすくなった
* テストが失敗したときのアサーションルーレットを避けるために
** カスタムアサーションの引数に文字列を加えた
** どのオブジェクトをテストしていたか分かりやすい
This simplified test was made possible by having the following Custom
Assertion available to us:
// static void assertLineItemsEqual(
// String msg, LineItem exp, LineItem act) {
// assertEquals(msg+" Inv", exp. getInv(),act. getInv() );
// assertEquals(msg+" Prod", exp. getProd(), act. getProd()) ;
// assertEquals(msg+" Quan", exp. getQuantity() , act. getQuantity()) ;
// }
This Custom Assertion compares the same attributes of the object as we
were comparing on an in-line basis in the previous version of the
test; thus the semantics of the test haven’t changed. We also
concatenate the name of the attribute being compared with the message
parameter to get a unique failure message, which allows us to avoid
playing Assertion Roulette when a test fails.
*このカスタムアサーションの説明
** 最初のバージョン(直書き)と同様に、オブジェクトのいくつかの属性だけを比較する
** 意味論的には変わってない
** 比較される属性をパラメータのメッセージと連結してユニークなエラーメッセージ
*** アサーションルーレットを回避
!!! Example: Domain Assertion
In this next version of the test, we have further elevated the level
of the assertions to better communicate the expected outcome of the
test scenarios:
// public void testAddOneLineItem_quantity1() {
// Invoice inv = createAnonInvoice();
// LineItem expItem = new LineItem(inv, product, QUANTITY) ;
// // Exercise
// inv.addItemQuantity( product, QUANTITY);
// // Verify
// assertInvoiceContainsOnlyThisLineItem( inv, expItem);
// }
// public void testRemoveLineItemsForProduct_oneOfTwo_() {
// Invoice inv = createAnonInvoice();
// inv.addItemQuantity( product, QUANTITY);
// inv.addItemQuantity( anotherProduct, QUANTITY);
// LineItem expItem = new LineItem( inv, product, QUANTITY);
// // Exercise
// inv.removeLineItemForProduct( anotherProduct );
// // Verify
// assertInvoiceContainsOnlyThisLineItem( inv, expItem);
// }
This simplified version of the test was made possible by extracting
the following Domain Assertion method:
// void assertInvoiceContainsOnlyThisLineItem(
// Invoice inv,
// LineItem expItem) {
// List lineItems = inv. getLineItems();
// assertEquals("number of items", lineItems. size(), 1);
// LineItem actual= (LineItem) lineItems. get(0);
// assertLineItemsEqual("item", expItem, actual);
// }
This example chose to forgo passing a message to the Domain Assertion
to save a bit of space. In real life, we would typically include a
message string in the parameter list and concatenate the messages of
the individual assertions to one passed in. See Assertion Message
(page 370) for more details.
!!! Example: verification Method
If the exercise SUT and result verification phases of several tests
are pretty much identical, we can incorporate both phases into our
reusable Custom Assertion. Because this approach changes the
semantics of the Custom Assertion from being just a function free of
side effects to an operation that changes the state of the SUT, we
usually give it a more distinctive name starting with “verify”.
*SUT の exercise フェーズと結果検証フェーズが独立している
**両方のフェーズをカスタムアサーション(再利用可)に組込むことができる
*カスタムアサーションのセマンティクスを変える
**前:副作用のない関数
**後:SUTの状態を変えるオペレーション
*名前
**"verify"から始まる名前にするのが通例
This version of the test merely sets up the test fixture before
calling a verification Method that incorporates both the exercise SUT
and verify outcome phases of the test. It is most easily recognized by
the lack of a distinct “exercise” phase in the calling test and the
presence of calls to methods that modify the state of one of the
objects passed as a parameter of the verification Method.
*今回のテストコード
**フィクスチャのセットアップと verification Method の呼び出しだけしかない
**verification Method が exercise フェーズを内包しているから
// public void testAddOneLineItem_quantity2() {
// Invoice inv = createAnonInvoice();
// LineItem expItem = new LineItem(inv, product, QUANTITY) ;
// // Exercise & Verify
// verifyOneLineItemCanBeAdded(inv, product, QUANTITY, expItem);
// }
// public void verifyOneLineItemCanBeAdded(
// Invoice inv, Product product,
// int QUANTITY, LineItem expItem) {
// // Exercise
// inv.addItemQuantity(product, QUANTITY);
// // Verify
// assertInvoiceContainsOnlyThisLineItem(inv, expItem) ;
// }
This verification Method calls the “pure” Custom Assertion, although
it could just as easily have included all the assertion logic if we
didn’t have the other Custom Assertion to call. Note the call to
addItemQuantity on the parameter inv; this is what changes if from a
Custom Assertion to a verification Method.
*単なるカスタムアサーションの呼び出しだけ
*引数の inv の addItemQuantity メソッドを呼び出しているところに注目
!!! Example: Custom Assertion Test
This Custom Assertion isn’t particularly complicated, so we may feel
comfortable without having any automated tests for it. If there is
anything complex about it, however, we may find it worthwhile to write
tests like these:
// public void testassertLineItemsEqual_equivalent() {
// Invoice inv = createAnonInvoice();
// LineItem item1 = new LineItem(inv, product, QUANTITY1);
// LineItem item2 = new LineItem(inv, product, QUANTITY1);
// // exercise/verify
// assertLineItemsEqual("This should not fail", item1, item2) ;
// }
// public void testassertLineItemsEqual_differentInvoice() {
// Invoice inv1 = createAnonInvoice();
// Invoice inv2 = createAnonInvoice();
// LineItem item1 = new LineItem(inv1, product, QUANTITY1) ;
// LineItem item2 = new LineItem(inv2, product, QUANTITY1) ;
// // exercise/verify
// try {
// assertLineItemsEqual("Msg", item1, item2) ;
// } catch (AssertionFailedError e) {
// assertEquals("e. getMsg",
// "Invoice-expected: <123> but was <124>",
// e. getMessage());
// return;
// }
// fail("Should have thrown exception");
// }
// public void testassertLineItemsEqual_differentQuantity() {
// Invoice inv = createAnonInvoice();
// LineItem item1 = new LineItem(inv, product, QUANTITY1);
// LineItem item2 = new LineItem(inv, product, QUANTITY2);
// // exercise/verify
// try {
// assertLineItemsEqual("Msg", item1, item2) ;
// } catch (AssertionFailedError e) {
// pass(); // to indicate that no assertion is needed
// return;
// }
// fail("Should have thrown exception");
// }
This example includes a few of the Custom Assertion Tests needed for
this Custom Assertion. Note that the code includes one “equivalent”
and several “different” tests (one for each attribute whose
difference should cause the test to fail). We have to use the second
form of the Expected Exception Test template in those cases where the
assertion was expected to fail, because fail throws the same exception
as our assertion method. In one of the “different” tests, we have
included sample logic for asserting on the exception
message. (Although I’ve abridged it to save space, the example here
should give you an idea of where to assert on the message.)
*この例にはカスタムアサーションテストに必要なものが含まれている
**1 つの "equivalent" テスト
**いくつかの "diffrent" テスト
***テストを失敗させるために必要な属性について
*Expected Exception Testの雛形として 2 番目を使っている
** アサーションメソッドが例外をスローするから
*メッセージは要約してる