-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathch11-01-writing-tests.html
943 lines (801 loc) · 86.8 KB
/
ch11-01-writing-tests.html
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
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
<!DOCTYPE HTML>
<html lang="uk" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Як Писати Тести - Мова програмування Rust</title>
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="title-page.html">Мова Програмування Rust</a></li><li class="chapter-item expanded affix "><a href="foreword.html">Передмова</a></li><li class="chapter-item expanded affix "><a href="ch00-00-introduction.html">Вступ</a></li><li class="chapter-item expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> Початок</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> Встановлення</a></li><li class="chapter-item expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="chapter-item expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Привіт, Cargo!</a></li></ol></li><li class="chapter-item expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> Програмування Гри Відгадайки</a></li><li class="chapter-item expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> Загальні Концепції Програмування</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> Змінні і Мутабельність</a></li><li class="chapter-item expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> Типи Даних</a></li><li class="chapter-item expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> Функції</a></li><li class="chapter-item expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> Коментарі</a></li><li class="chapter-item expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> Потік Виконання</a></li></ol></li><li class="chapter-item expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> Розуміння Володіння</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> Що Таке Володіння?</a></li><li class="chapter-item expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> Посилання та Позичання</a></li><li class="chapter-item expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> Слайси</a></li></ol></li><li class="chapter-item expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> Використання Структур для Групування Пов'язаних Даних</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> Визначення та Створення Екземпляра Структури</a></li><li class="chapter-item expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> Приклад Програми з Використанням Структур</a></li><li class="chapter-item expanded "><a href="ch05-03-method-syntax.html"><strong aria-hidden="true">5.3.</strong> Синтаксис Методів</a></li></ol></li><li class="chapter-item expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> Енуми та Зіставлення зі Шаблоном</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch06-01-defining-an-enum.html"><strong aria-hidden="true">6.1.</strong> Визначення Енума</a></li><li class="chapter-item expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> Конструкція Потоку Виконання match</a></li><li class="chapter-item expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> Лаконічний Потік Виконання з if let</a></li></ol></li><li class="chapter-item expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> Керування Щораз Більшими Проєктами із Пакетами, Крейтами та Модулями</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> Пакети та Крейти</a></li><li class="chapter-item expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> Визначення Модулів для Контролю Області Видимості та Приватності</a></li><li class="chapter-item expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html"><strong aria-hidden="true">7.3.</strong> Шлях для Доступу до Елементів у Дереві Модулів</a></li><li class="chapter-item expanded "><a href="ch07-04-bringing-paths-into-scope-with-the-use-keyword.html"><strong aria-hidden="true">7.4.</strong> Введення Шляхів до Області Видимості з Ключовим Словом use</a></li><li class="chapter-item expanded "><a href="ch07-05-separating-modules-into-different-files.html"><strong aria-hidden="true">7.5.</strong> Розподіл Модулів на Різні Файли</a></li></ol></li><li class="chapter-item expanded "><a href="ch08-00-common-collections.html"><strong aria-hidden="true">8.</strong> Звичайні Колекції</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch08-01-vectors.html"><strong aria-hidden="true">8.1.</strong> Зберігання Списків Значень з Векторами</a></li><li class="chapter-item expanded "><a href="ch08-02-strings.html"><strong aria-hidden="true">8.2.</strong> Зберігання Тексту у Кодуванні UTF-8 в Стрічках</a></li><li class="chapter-item expanded "><a href="ch08-03-hash-maps.html"><strong aria-hidden="true">8.3.</strong> Зберігання Ключів з Асоційованими Значеннями у Хеш-Мапах</a></li></ol></li><li class="chapter-item expanded "><a href="ch09-00-error-handling.html"><strong aria-hidden="true">9.</strong> Обробка Помилок</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch09-01-unrecoverable-errors-with-panic.html"><strong aria-hidden="true">9.1.</strong> Невідновлювані Помилки з panic!</a></li><li class="chapter-item expanded "><a href="ch09-02-recoverable-errors-with-result.html"><strong aria-hidden="true">9.2.</strong> Відновлювані Помилки з Result</a></li><li class="chapter-item expanded "><a href="ch09-03-to-panic-or-not-to-panic.html"><strong aria-hidden="true">9.3.</strong> panic! чи не panic!</a></li></ol></li><li class="chapter-item expanded "><a href="ch10-00-generics.html"><strong aria-hidden="true">10.</strong> Узагальнені Типи, Трейти та Часи Існування</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch10-01-syntax.html"><strong aria-hidden="true">10.1.</strong> Узагальнені Типи Даних</a></li><li class="chapter-item expanded "><a href="ch10-02-traits.html"><strong aria-hidden="true">10.2.</strong> Трейти: Визначення Спільної Поведінки</a></li><li class="chapter-item expanded "><a href="ch10-03-lifetime-syntax.html"><strong aria-hidden="true">10.3.</strong> Перевірка Коректності Посилань із Часами Існування</a></li></ol></li><li class="chapter-item expanded "><a href="ch11-00-testing.html"><strong aria-hidden="true">11.</strong> Написання Автоматизованих Тестів</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch11-01-writing-tests.html" class="active"><strong aria-hidden="true">11.1.</strong> Як Писати Тести</a></li><li class="chapter-item expanded "><a href="ch11-02-running-tests.html"><strong aria-hidden="true">11.2.</strong> Керування Запуском Тестів</a></li><li class="chapter-item expanded "><a href="ch11-03-test-organization.html"><strong aria-hidden="true">11.3.</strong> Організація Тестів</a></li></ol></li><li class="chapter-item expanded "><a href="ch12-00-an-io-project.html"><strong aria-hidden="true">12.</strong> Проєкт з Вводом/Виводом: Створення Програми з Інтерфейсом Командного Рядка</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch12-01-accepting-command-line-arguments.html"><strong aria-hidden="true">12.1.</strong> Приймання Аргументів Командного Рядка</a></li><li class="chapter-item expanded "><a href="ch12-02-reading-a-file.html"><strong aria-hidden="true">12.2.</strong> Читання Файлу</a></li><li class="chapter-item expanded "><a href="ch12-03-improving-error-handling-and-modularity.html"><strong aria-hidden="true">12.3.</strong> Рефакторинг для Покращення Модульності та Обробки Помилок</a></li><li class="chapter-item expanded "><a href="ch12-04-testing-the-librarys-functionality.html"><strong aria-hidden="true">12.4.</strong> Розробка Функціонала Бібліотеки із Test-Driven Development</a></li><li class="chapter-item expanded "><a href="ch12-05-working-with-environment-variables.html"><strong aria-hidden="true">12.5.</strong> Робота зі Змінними Середовища</a></li><li class="chapter-item expanded "><a href="ch12-06-writing-to-stderr-instead-of-stdout.html"><strong aria-hidden="true">12.6.</strong> Написання Повідомлень про Помилки у Помилковий Вивід замість Стандартного Виводу</a></li></ol></li><li class="chapter-item expanded "><a href="ch13-00-functional-features.html"><strong aria-hidden="true">13.</strong> Функціональні Можливості Мови: Ітератори та Замикання</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch13-01-closures.html"><strong aria-hidden="true">13.1.</strong> Замикання: Анонімні Функції, що Захоплюють Своє Середовище</a></li><li class="chapter-item expanded "><a href="ch13-02-iterators.html"><strong aria-hidden="true">13.2.</strong> Обробка Послідовностей Елементів з Ітераторами</a></li><li class="chapter-item expanded "><a href="ch13-03-improving-our-io-project.html"><strong aria-hidden="true">13.3.</strong> Покращення Нашого Проєкту з Вводом/Виводом</a></li><li class="chapter-item expanded "><a href="ch13-04-performance.html"><strong aria-hidden="true">13.4.</strong> Порівняння Швидкодії: Цикли Проти Ітераторів</a></li></ol></li><li class="chapter-item expanded "><a href="ch14-00-more-about-cargo.html"><strong aria-hidden="true">14.</strong> Більше про Cargo та Crates.io</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch14-01-release-profiles.html"><strong aria-hidden="true">14.1.</strong> Налаштування Збірок з Release Профілями</a></li><li class="chapter-item expanded "><a href="ch14-02-publishing-to-crates-io.html"><strong aria-hidden="true">14.2.</strong> Публікація Крейта на Crates.io</a></li><li class="chapter-item expanded "><a href="ch14-03-cargo-workspaces.html"><strong aria-hidden="true">14.3.</strong> Робочі Області Cargo</a></li><li class="chapter-item expanded "><a href="ch14-04-installing-binaries.html"><strong aria-hidden="true">14.4.</strong> Встановлення Двійкових Файлів з cargo install</a></li><li class="chapter-item expanded "><a href="ch14-05-extending-cargo.html"><strong aria-hidden="true">14.5.</strong> Розширення Cargo із Користувацькими Командами</a></li></ol></li><li class="chapter-item expanded "><a href="ch15-00-smart-pointers.html"><strong aria-hidden="true">15.</strong> Розумні Вказівники</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch15-01-box.html"><strong aria-hidden="true">15.1.</strong> Використання Box<T> для Вказування на Дані в Купі</a></li><li class="chapter-item expanded "><a href="ch15-02-deref.html"><strong aria-hidden="true">15.2.</strong> Ставлення до Розумних Вказівників як до Звичайних Посилань з Трейтом Deref</a></li><li class="chapter-item expanded "><a href="ch15-03-drop.html"><strong aria-hidden="true">15.3.</strong> Виконання Коду при Очищенні з Трейтом Drop</a></li><li class="chapter-item expanded "><a href="ch15-04-rc.html"><strong aria-hidden="true">15.4.</strong> Rc<T> - Розумний Вказівник з Лічильником Посилань</a></li><li class="chapter-item expanded "><a href="ch15-05-interior-mutability.html"><strong aria-hidden="true">15.5.</strong> RefCell<T> та Шаблон Внутрішньої Мутабельності</a></li><li class="chapter-item expanded "><a href="ch15-06-reference-cycles.html"><strong aria-hidden="true">15.6.</strong> Цикли Посилань Можуть Спричинити Витік Пам'яті</a></li></ol></li><li class="chapter-item expanded "><a href="ch16-00-concurrency.html"><strong aria-hidden="true">16.</strong> Безстрашна Конкурентність</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch16-01-threads.html"><strong aria-hidden="true">16.1.</strong> Використання Потоків для Одночасного Виконання Коду</a></li><li class="chapter-item expanded "><a href="ch16-02-message-passing.html"><strong aria-hidden="true">16.2.</strong> Застосування Обміну Повідомлень для Передавання Даних між Потоками</a></li><li class="chapter-item expanded "><a href="ch16-03-shared-state.html"><strong aria-hidden="true">16.3.</strong> Конкурентність зі Спільним Станом</a></li><li class="chapter-item expanded "><a href="ch16-04-extensible-concurrency-sync-and-send.html"><strong aria-hidden="true">16.4.</strong> Розширювана Конкурентність із Трейтами Sync та Send</a></li></ol></li><li class="chapter-item expanded "><a href="ch17-00-oop.html"><strong aria-hidden="true">17.</strong> Особливості Об'єктоорієнтованого Програмування в Rust</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch17-01-what-is-oo.html"><strong aria-hidden="true">17.1.</strong> Характеристики Об'єктоорієнтованих Мов</a></li><li class="chapter-item expanded "><a href="ch17-02-trait-objects.html"><strong aria-hidden="true">17.2.</strong> Використання Трейт-Об'єктів, які Допускають Значення Різних Типів</a></li><li class="chapter-item expanded "><a href="ch17-03-oo-design-patterns.html"><strong aria-hidden="true">17.3.</strong> Реалізація Об'єктоорієнтованого Шаблону Проєктування</a></li></ol></li><li class="chapter-item expanded "><a href="ch18-00-patterns.html"><strong aria-hidden="true">18.</strong> Шаблони та Зіставлення Шаблонів</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch18-01-all-the-places-for-patterns.html"><strong aria-hidden="true">18.1.</strong> Усі Місця Можливого Використання Шаблонів</a></li><li class="chapter-item expanded "><a href="ch18-02-refutability.html"><strong aria-hidden="true">18.2.</strong> Спростовуваність: Чи Може Шаблон Бути Невідповідним</a></li><li class="chapter-item expanded "><a href="ch18-03-pattern-syntax.html"><strong aria-hidden="true">18.3.</strong> Синтаксис Шаблонів</a></li></ol></li><li class="chapter-item expanded "><a href="ch19-00-advanced-features.html"><strong aria-hidden="true">19.</strong> Просунуті Можливості</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch19-01-unsafe-rust.html"><strong aria-hidden="true">19.1.</strong> Небезпечний Rust</a></li><li class="chapter-item expanded "><a href="ch19-03-advanced-traits.html"><strong aria-hidden="true">19.2.</strong> Поглиблено про Трейти</a></li><li class="chapter-item expanded "><a href="ch19-04-advanced-types.html"><strong aria-hidden="true">19.3.</strong> Поглиблено про Типи</a></li><li class="chapter-item expanded "><a href="ch19-05-advanced-functions-and-closures.html"><strong aria-hidden="true">19.4.</strong> Поглиблено про Функції та Замикання</a></li><li class="chapter-item expanded "><a href="ch19-06-macros.html"><strong aria-hidden="true">19.5.</strong> Макроси</a></li></ol></li><li class="chapter-item expanded "><a href="ch20-00-final-project-a-web-server.html"><strong aria-hidden="true">20.</strong> Останній Проєкт: Збірка Багатопотокового Вебсервера</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch20-01-single-threaded.html"><strong aria-hidden="true">20.1.</strong> Збірка Однопотокового Вебсервера</a></li><li class="chapter-item expanded "><a href="ch20-02-multithreaded.html"><strong aria-hidden="true">20.2.</strong> Перетворюємо Наш Однопотоковий Сервер на Багатопотоковий</a></li><li class="chapter-item expanded "><a href="ch20-03-graceful-shutdown-and-cleanup.html"><strong aria-hidden="true">20.3.</strong> Плавне Вимкнення та Очищення</a></li></ol></li><li class="chapter-item expanded "><a href="appendix-00.html"><strong aria-hidden="true">21.</strong> Додатки</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="appendix-01-keywords.html"><strong aria-hidden="true">21.1.</strong> A - Ключові Слова</a></li><li class="chapter-item expanded "><a href="appendix-02-operators.html"><strong aria-hidden="true">21.2.</strong> B - Оператори та Символи</a></li><li class="chapter-item expanded "><a href="appendix-03-derivable-traits.html"><strong aria-hidden="true">21.3.</strong> C - Похідні Трейти</a></li><li class="chapter-item expanded "><a href="appendix-04-useful-development-tools.html"><strong aria-hidden="true">21.4.</strong> D - Корисні Інструменти Розробки</a></li><li class="chapter-item expanded "><a href="appendix-05-editions.html"><strong aria-hidden="true">21.5.</strong> E - Видання</a></li><li class="chapter-item expanded "><a href="appendix-06-translation.html"><strong aria-hidden="true">21.6.</strong> F - Переклади Книги</a></li><li class="chapter-item expanded "><a href="appendix-07-nightly-rust.html"><strong aria-hidden="true">21.7.</strong> G - як Розробляється Rust і "Нічний Rust"</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Мова програмування Rust</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h2 id="Як-Писати-Тести"><a class="header" href="#Як-Писати-Тести">Як Писати Тести</a></h2>
<p>Тести - це функції Rust, які перевіряють, чи тестований код працює у очікуваний спосіб. Тіла тестових функцій зазвичай виконують наступні три дії:</p>
<ol>
<li>Встановити будь-яке потрібне значення або стан.</li>
<li>Запустити на виконання код, який ви хочете протестувати.</li>
<li>Переконатися, що отримані результати відповідають вашим очікуванням.</li>
</ol>
<p>Розгляньмо функціонал, наданий Rust спеціально для написання тестів та виконання зазначених дій, що включає атрибут <code>test</code>, декілька макросів, а також атрибут <code>should_panic</code>.</p>
<h3 id="Анатомія-Тестувальної-Функції"><a class="header" href="#Анатомія-Тестувальної-Функції">Анатомія Тестувальної Функції</a></h3>
<p>У найпростішому випадку тест у Rust - це функція, анотована за допомогою атрибута <code>test</code>. Атрибути - це метадані фрагментів коду на Rust; прикладом може бути атрибут <code>derive</code>, який ми використовували зі структурами у Розділі 5. Для перетворення звичайної функції на тестувальну функцію додайте <code>#[test]</code> у рядок перед <code>fn</code>. Коли ви запускаєте ваші тести командою <code>cargo test</code>, Rust збирає двійковий файл, що запускає анотовані функції та звітує, чи тестові функції пройшли, чи провалилися.</p>
<p>Кожного разу, коли ми створюємо новий бібліотечний проєкт за допомогою Cargo, він автоматично генерує для нас тестовий модуль з тестовими функціями. Цей модуль надає вам шаблон для написання тестів, а отже вам непотрібно кожного разу при створенні нового проєкту уточнювати їхню структуру та синтаксис. Ви можете додати стільки тестових модулів та тестових функцій, скільки забажаєте!</p>
<p>Перш ніж фактично протестувати будь-який код, ми розглянемо деякі аспекти роботи тестів, експериментуючи з шаблоном тесту. Потім ми напишемо декілька реальних тестів, що запускають наш код та підтверджують, що його поведінка є правильною.</p>
<p>Створімо новий бібліотечний проєкт під назвою <code>adder</code>, в якому додаються два числа:</p>
<pre><code class="language-console">$ cargo new adder --lib
Created library `adder` project
$ cd adder
</code></pre>
<p>Вміст файлу <em>src/lib.rs</em> у вашій бібліотеці <code>adder</code> має виглядати, як показано в Блоці коду 11-1.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<!-- manual-regeneration
cd listings/ch11-writing-automated-tests
rm -rf listing-11-01
cargo new listing-11-01 --lib --name adder
cd listing-11-01
cargo test
git co output.txt
cd ../../..
-->
<pre><code class="language-rust noplayground">#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}
</code></pre>
<p><span class="caption">Блок коду 11-1: тестовий модуль і функція, створена автоматично за допомогою <code>cargo new</code></span></p>
<p>Наразі проігноруймо два верхні рядки та зосередимося на функції. Зверніть увагу на анотацію <code>#[test]</code>: цей атрибут вказує на те, що функція є тестувальною, тож функціонал для запуску тестів ставитиметься до неї, як до тесту. Ми також можемо мати нетестувальні функції в модулі <code>tests</code>, щоб допомогти налаштувати типові сценарії або виконати загальні операції, тому ми завжди повинні позначати анотаціями, які саме функції є тестувальними.</p>
<p>Тіло функції зі приклада використовує макрос <code>assert_eq!</code> для ствердження того, що <code>result</code>, який містить результат операції додавання 2 та 2, дорівнює 4. Це ствердження служить типовим зразком формату для тесту. Запустімо його та впевнимось, що тест проходить.</p>
<p>Команда <code>cargo test</code> запускає усі тести з нашого проєкту, як показано у Блоці коду 11-2.</p>
<pre><code class="language-console">$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.57s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
running 1 test
test tests::it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests adder
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</code></pre>
<p><span class="caption">Блок коду 11-2: виведення після запуску автоматично згенерованого тесту</span></p>
<p>Cargo скомпілював та запустив тест. Ми бачимо рядок <code>running 1 test</code>. Наступний рядок показує назву згенерованої тестувальної функції <code>it_works</code>, а також що результат запуску тесту є <code>ok</code>. Загальний результат <code>test result: ok.</code> означає, що усі тести пройшли, а частина <code>1 passed; 0 failed</code> показує загальну кількість тестів що були пройдені та провалилися.</p>
<p>Можна позначити деякі тести як ігноровані, тоді вони не будуть запускатися; ми розглянемо це далі у цьому розділі у підрозділі<a href="ch11-02-running-tests.html#ignoring-some-tests-unless-specifically-requested"> "Ігнорування окремих тестів без спеціального уточнення"</a><!-- ignore --> . Оскільки ми не позначали жодного тесту для ігнорування, то отримуємо на виході <code>0 ignored</code>. Ми також можемо додати до команди <code>cargo test</code> аргумент, щоб запускалися лише ті тести, що відповідають певній стрічці; Це називається <em>фільтрацією</em> та ми поговоримо про це у підрозділі <a href="ch11-02-running-tests.html#running-a-subset-of-tests-by-name">"Запуск підмножини тестів за назвою"</a><!-- ignore --> . Ми також не маємо відфільтрованих тестів, тому вивід показує <code>0 filtered out</code>.</p>
<p><code>0 measured</code> показує статистику бенчмарків, що тестують швидкодію. Бенчмарки на момент написання цієї статті доступні лише у нічних збірках Rust. Дивись <a href="../unstable-book/library-features/test.html">документацію про бенчмарки</a> для більш детальної інформації.</p>
<p>Наступна частина виводу <code>Doc-tests adder</code> призначена для результатів документаційних тестів. У нас поки що немає документаційних тестів, але Rust може скомпілювати будь-які приклади коду з документації по нашому API. Ця функція допомагає синхронізувати вашу документацію та код! Ми розглянемо, як писати документаційні тести в підрозділі <a href="ch14-02-publishing-to-crates-io.html#documentation-comments-as-tests">“Документаційні коментарі як тести”</a><!-- ignore --> Розділу 14. Зараз ми проігноруємо частину виводу, присвячену <code>Doc-tests</code>.</p>
<p>Налаштуймо тест для відповідності нашим потребам. Спочатку змінимо назву тестової функції <code>it_works</code> на іншу, наприклад <code>exploration</code>, ось так:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground">#[cfg(test)]
mod tests {
#[test]
fn exploration() {
assert_eq!(2 + 2, 4);
}
}
</code></pre>
<p>Далі знову запустимо <code>cargo test</code>. Вивід тепер покаже <code>exploration</code> замість <code>it_works</code>:</p>
<pre><code class="language-console">$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.59s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
running 1 test
test tests::exploration ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests adder
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</code></pre>
<p>Тепер ми додамо інший тест, але цього разу він завершиться зі збоєм! Тести завершуються зі збоєм, коли щось у тестувальній функції викликає паніку. Кожний тест запускається в окремому потоці, та коли головний потік бачить, що тестовий потік упав, то тест позначається як такий невдалий. У Розділі 9 ми розглядали найпростіший спосіб викликати паніку за допомогою виклику макросу <code>panic!</code>. Створіть новий тест та назвіть тестувальну функцію <code>another</code>, щоб ваш файл <em>src/lib.rs</em> виглядав як у Блоці коду 11-3.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust panics noplayground">#[cfg(test)]
mod tests {
#[test]
fn exploration() {
assert_eq!(2 + 2, 4);
}
#[test]
fn another() {
panic!("Make this test fail");
}
}
</code></pre>
<p><span class="caption">Блок коду 11-3: додавання другого тесту, що провалюється, бо ми викликаємо макрос <code>panic!</code></span></p>
<p>Запустіть тест знову, використовуючи <code>cargo test</code>. Вивід виглядатиме схоже на Блок коду 11-4, який показує, що тест <code>exploration</code> пройшов, а <code>another</code> провалився.</p>
<pre><code class="language-console">$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.72s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
running 2 tests
test tests::another ... FAILED
test tests::exploration ... ok
failures:
---- tests::another stdout ----
thread 'main' panicked at 'Make this test fail', src/lib.rs:10:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::another
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
</code></pre>
<p><span class="caption">Блок коду 11-4: результати тестів, коли один тест проходить, а другий провалюється</span></p>
<p>Замість <code>ok</code>, рядок <code>test tests::another</code> показує <code>FAILED</code>. Дві нові секції з'явилися між результатами окремих тестів та загальними результатами: перша показує детальну причину того, чому тест провалився. У цьому випадку ми отримали те, що тест <code>another</code> провалився тому, що <code>panicked at 'Make this test fail'</code> у рядку 10 у файлі <em>src/lib.rs</em>. У наступній секції наведені назви тестів, що провалилися, і це зручно коли у нас багато таких тестів та багато деталей про провали. Ми можемо використати назву тесту для його подальшого зневадження; ми поговоримо більше про запуск тестів у підрозділі <a href="">Керування запуском тестів"</a><!-- ignore
--> .</p>
<p>У кінці показується підсумковий результат тестування: в цілому результат нашого тесту <code>FAILED</code>. У нас один тест пройшов, та один провалився.</p>
<p>Тепер, коли ви побачили, як виглядають результати тесту в різних сценаріях, розгляньмо деякі макроси, крім <code>panic!</code>, які корисні в тестах.</p>
<h3 id="Перевірка-Результатів-із-Макросом-assert"><a class="header" href="#Перевірка-Результатів-із-Макросом-assert">Перевірка Результатів із Макросом <code>assert!</code></a></h3>
<p>Макрос <code>assert!</code>, що надається стандартною бібліотекою, широко використовується для того, щоб впевнитися, що деяка умова у тесті приймає значення <code>true</code>. Ми даємо макросу <code>assert!</code> аргумент, який обчислюється як вираз булевого типу. Якщо значення обчислюється як <code>true</code>, нічого поганого не трапляється та тест вважається пройденим. Якщо ж значення буде <code>false</code> макрос <code>assert!</code> викликає паніку <code>panic!</code>, що спричиняє провал тесту. Використання макросу <code>assert!</code> допомагає нам перевірити, чи працює наш код в очікуваний спосіб.</p>
<p>У Розділі 5, Блок коду 5-15, ми використовували структуру <code>Rectangle</code> та метод <code>can_hold</code>, які повторюються у Блоці коду 11-5. Розмістімо цей код у файлі <em>src/lib.rs</em>, а далі напишемо декілька тестів, використовуючи макрос <code>assert!</code>.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground">#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
</code></pre>
<p><span class="caption">Блок коду 11-5: використання структури <code>Rectangle</code> і її методу <code>can_hold</code> з Розділу 5</span></p>
<p>Метод <code>can_hold</code> повертає булеве значення, що означає, що це ідеальний варіант використання для макросу <code>assert!</code>. У Блоці коду 11-6 ми пишемо тест, який перевіряє метод <code>can_hold</code>, створивши екземпляр <code>Rectangle</code>, що має ширину 8 і висоту 7, і стверджує, що він може вмістити інший екземпляр <code>Rectangle</code>, що має ширину 5 і висоту 1.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Rectangle {
</span><span class="boring"> width: u32,
</span><span class="boring"> height: u32,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Rectangle {
</span><span class="boring"> fn can_hold(&self, other: &Rectangle) -> bool {
</span><span class="boring"> self.width > other.width && self.height > other.height
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>#[cfg(test)]
mod tests {
use super::*;
#[test]
fn larger_can_hold_smaller() {
let larger = Rectangle {
width: 8,
height: 7,
};
let smaller = Rectangle {
width: 5,
height: 1,
};
assert!(larger.can_hold(&smaller));
}
}
</code></pre>
<p><span class="caption">Блок коду 11-6: тест для <code>can_hold</code>, що перевіряє, чи більший прямокутник справді може вмістити менший</span></p>
<p>Зверніть увагу, що ми додали новий рядок всередині модуля <code>tests</code>: <code>use super::*;</code>. Модуль <code>tests</code> є звичайним модулем, що слідує звичайним правилам видимості, про які ми розповідали у підрозділі <a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html">“Шляхи для посилання на елемент в дереві модулів”</a><!-- ignore -->
Розділу 7. Оскільки модуль <code>tests</code> є внутрішнім модулем, нам потрібно ввести код для тестування з зовнішнього модуля до області видимості внутрішнього модуля. Ми використовуємо глобальний режим для того, щоб все, що визначене у зовнішньому модулі, було доступним к модулі <code>tests</code>.</p>
<p>Ми назвали наш тест <code>larger_can_hold_smaller</code>і створили потрібні нам два екземпляри <code>Rectangle</code>. Тоді ми викликали макрос <code>assert!</code> і передали йому результат виклику <code>larger.can_hold(&smaller)</code>. Цей вираз повинен повернути <code>true</code>, тому наш тест повинен пройти. З'ясуймо, чи це так!</p>
<pre><code class="language-console">$ cargo test
Compiling rectangle v0.1.0 (file:///projects/rectangle)
Finished test [unoptimized + debuginfo] target(s) in 0.66s
Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
running 1 test
test tests::larger_can_hold_smaller ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests rectangle
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</code></pre>
<p>Цей тест проходить! Додамо ще один тест, цього разу стверджуючи, що менший прямокутник не може вміститися в більшому:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Rectangle {
</span><span class="boring"> width: u32,
</span><span class="boring"> height: u32,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Rectangle {
</span><span class="boring"> fn can_hold(&self, other: &Rectangle) -> bool {
</span><span class="boring"> self.width > other.width && self.height > other.height
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>#[cfg(test)]
mod tests {
use super::*;
#[test]
fn larger_can_hold_smaller() {
// --snip--
<span class="boring"> let larger = Rectangle {
</span><span class="boring"> width: 8,
</span><span class="boring"> height: 7,
</span><span class="boring"> };
</span><span class="boring"> let smaller = Rectangle {
</span><span class="boring"> width: 5,
</span><span class="boring"> height: 1,
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> assert!(larger.can_hold(&smaller));
</span> }
#[test]
fn smaller_cannot_hold_larger() {
let larger = Rectangle {
width: 8,
height: 7,
};
let smaller = Rectangle {
width: 5,
height: 1,
};
assert!(!smaller.can_hold(&larger));
}
}
</code></pre>
<p>Оскільки правильний результат функції <code>can_hold</code> у цьому випадку <code>false</code>, нам необхідно обернути результат перед тим, як передати його до макросу <code>assert!</code>. В результаті наш тест пройде, якщо <code>can_hold</code> повертає <code>false</code>:</p>
<pre><code class="language-console">$ cargo test
Compiling rectangle v0.1.0 (file:///projects/rectangle)
Finished test [unoptimized + debuginfo] target(s) in 0.66s
Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
running 2 tests
test tests::larger_can_hold_smaller ... ok
test tests::smaller_cannot_hold_larger ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests rectangle
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</code></pre>
<p>Проходять вже два тести! Тепер подивімося, що відбувається з результатами тесту, якщо в код додати ваду. Ми змінимо реалізацію методу <code>can_hold</code>, замінивши знак більше на менше при порівняння ширин:</p>
<pre><code class="language-rust not_desired_behavior noplayground"><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Rectangle {
</span><span class="boring"> width: u32,
</span><span class="boring"> height: u32,
</span><span class="boring">}
</span><span class="boring">
</span>// --snip--
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width < other.width && self.height > other.height
}
}
<span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn larger_can_hold_smaller() {
</span><span class="boring"> let larger = Rectangle {
</span><span class="boring"> width: 8,
</span><span class="boring"> height: 7,
</span><span class="boring"> };
</span><span class="boring"> let smaller = Rectangle {
</span><span class="boring"> width: 5,
</span><span class="boring"> height: 1,
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> assert!(larger.can_hold(&smaller));
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn smaller_cannot_hold_larger() {
</span><span class="boring"> let larger = Rectangle {
</span><span class="boring"> width: 8,
</span><span class="boring"> height: 7,
</span><span class="boring"> };
</span><span class="boring"> let smaller = Rectangle {
</span><span class="boring"> width: 5,
</span><span class="boring"> height: 1,
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> assert!(!smaller.can_hold(&larger));
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p>Запуск тестів тепер виводить таке:</p>
<pre><code class="language-console">$ cargo test
Compiling rectangle v0.1.0 (file:///projects/rectangle)
Finished test [unoptimized + debuginfo] target(s) in 0.66s
Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
running 2 tests
test tests::larger_can_hold_smaller ... FAILED
test tests::smaller_cannot_hold_larger ... ok
failures:
---- tests::larger_can_hold_smaller stdout ----
thread 'main' panicked at 'assertion failed: larger.can_hold(&smaller)', src/lib.rs:28:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::larger_can_hold_smaller
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
</code></pre>
<p>Наші тести викрили ваду! Оскільки <code>larger.width</code> дорівнює 8, а <code>smaller.width</code> дорівнює 5, порівняння ширин у <code>can_hold</code> тепер повертає <code>false</code>: 8 не є меншим за 5.</p>
<h3 id="Перевірка-Рівності-з-Макросами-assert_eq-та-assert_ne"><a class="header" href="#Перевірка-Рівності-з-Макросами-assert_eq-та-assert_ne">Перевірка Рівності з Макросами <code>assert_eq!</code> та <code>assert_ne!</code></a></h3>
<p>Поширеним способом перевірки функціональності є перевірка на рівність між результатом коду, що тестується, і значенням, яке ви очікуєте від коду. Ви можете зробити це за допомогою макросу <code>assert!</code>, передавши йому вираз за допомогою оператора <code>==</code>. Однак це такий поширений тест, що стандартна бібліотека надає пару макросів — <code>assert_eq!</code> та <code>assert_ne!</code> — для зручнішого проведення цього тесту. Ці макроси порівнюють два аргументи на рівність або нерівність відповідно. Також вони виводять два значення, якщо ствердження провалюється, що допомагає зрозуміти, <em>чому</em> тест провалився; і навпаки, макрос <code>assert!</code> лише вказує на те, що отримав значення <code>false</code> для виразу <code>==</code>, без виведення значень, що призвели до цього <code>false</code>.</p>
<p>У Блоці коду 11-7 ми пишемо функцію з назвою <code>add_two</code>, яка додає до свого параметра <code>2</code>, а потім тестуємо цю функцію за допомогою макросу <code>assert_eq!</code>.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground">pub fn add_two(a: i32) -> i32 {
a + 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds_two() {
assert_eq!(4, add_two(2));
}
}
</code></pre>
<p><span class="caption">Блок коду 11-7: Тестування функції <code>add_two</code> за допомогою макросу <code>assert_eq!</code></span></p>
<p>Переконаймося, що вона проходить!</p>
<pre><code class="language-console">$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.58s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
running 1 test
test tests::it_adds_two ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests adder
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</code></pre>
<p>Ми передали <code>4</code> як аргумент <code>assert_eq!</code>, що дорівнює результату виклику <code>add_two(2)</code>. Рядок для цього тесту <code>test tests::it_adds_two ... ok</code>, і текст <code>ok</code> позначає, що наш тест пройшов!</p>
<p>Додамо в наш код ваду, щоб побачити, як виглядає <code>assert_eq!</code>, коли тест провалюється. Змініть реалізацію функції <code>add_two</code>, щоб натомість додавати <code>3</code>:</p>
<pre><code class="language-rust not_desired_behavior noplayground">pub fn add_two(a: i32) -> i32 {
a + 3
}
<span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn it_adds_two() {
</span><span class="boring"> assert_eq!(4, add_two(2));
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p>Запустимо тести знову:</p>
<pre><code class="language-console">$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.61s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
running 1 test
test tests::it_adds_two ... FAILED
failures:
---- tests::it_adds_two stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `4`,
right: `5`', src/lib.rs:11:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::it_adds_two
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
</code></pre>
<p>Наш тест виявив ваду! Тест <code>it_adds_two</code> провалився, і повідомлення каже нам про те, що провалене ствердження було <code>assertion failed: `(left == right)`</code> і значення <code>left</code> і <code>right</code>. Це повідомлення допомагає нам почати зневадження: аргумент <code>left</code> був <code>4</code>, але аргумент <code>right</code>, де стоїть <code>add_two(2)</code>, був <code>5</code>. Ви можете собі уявити, що це буде особливо корисно, коли у нас проводиться багато тестів.</p>
<p>Зверніть увагу, що в деяких мовах і тестувальних фреймворках параметри функції ствердження рівності називаються <code>expected</code> (очікувалося) і <code>actual</code> (фактично), і порядок, в якому ми вказуємо аргументи, важливий. Однак у Rust вони називаються <code>left</code> і <code>right</code>, і порядок, в якому ми вказуємо значення, яке ми очікуємо, і значення, обчислене кодом, не має значення. Ми могли б записати ствердження у цьому тесті як <code>assert_eq!(add_two(2), 4)</code>, що призведе до того ж повідомлення про помилку, яке показує <code>assertion failed: `(left == right)`</code>.</p>
<p>Макрос <code>assert_ne!</code> проходить тест, якщо два значення, які ми даємо, не дорівнюють одне одному, і провалюється, якщо вони рівні. Цей макрос найбільш корисний для випадків, коли ми не впевнені яким значення <em>буде</em>, але ми знаємо, яким значення безумовно <em>не повинне</em> бути. Наприклад, якщо ми тестуємо функцію, яка гарантовано певним чином змінює вхідні параметри, але те, як змінюється вони змінюються, залежить від дня тижня, коли ми проводимо свої тести, найкраще, що може стверджувати — що вихід функції не дорівнює вводу.</p>
<p>Під капотом макроси <code>assert_eq!</code> і <code>assert_ne!</code> використовують оператори <code>==</code> and <code>!=</code>, відповідно. Коли ствердження провалюється, ці макроси друкують свої аргументи з використанням форматування налагодження, тобто, що значення, які порівнюються, повинні реалізовувати трейти <code>PartialEq</code> і <code>Debug</code>. Всі примітивні типи та більшість типів зі стандартної бібліотеки реалізовують ці трейти. Для структур та енумів, які ви визначаєте самостійно, вам потрібно буде реалізувати <code>PartialEq</code>, щоб стверджувати рівність таких типів. Також потрібно буде реалізувати <code>Debug</code> для виведення значень, коли ствердження провалюється. Оскільки обидва ці трейти вивідні, як було зазначено в Блоці коду 5-12 у Розділі 5, зазвичай просто треба додати анотацію <code>#[derive(PartialEq, Debug)</code> до визначення вашої структури чи енуму. Дивіться Додаток C, <a href="appendix-03-derivable-traits.html">"Вивідні трейти",</a><!-- ignore --> для детальнішої інформації про ці та інші вивідні трейти.</p>
<h3 id="Додавання-Власних-Повідомлень-про-Збої"><a class="header" href="#Додавання-Власних-Повідомлень-про-Збої">Додавання Власних Повідомлень про Збої</a></h3>
<p>Ви також можете додати власне повідомлення, яке буде виведене в консолі разом із повідомленням про помилку, як додаткові аргументи до макросів <code>assert!</code>, <code>assert_eq!</code>, та <code>assert_ne!</code>. Будь-які аргументи, вказані після необхідних аргументів, передаються до макпрсу <code>format!</code> (обговорюється у Розділі 8 у підрозділі <a href="">"Конкатенація оператором <code>+</code> або макросом <code>format!</code>"</a><!-- ignore -->
), тож ви можете передати стрічку форматування, що містить заповнювачі <code>{}</code> та значення для підставляння в ці заповнювачі. Користувальницькі повідомлення корисні для документування, що означає ствердження; коли тест провалюється, ви будете мати краще розуміння того, що за проблема з кодом.</p>
<p>Наприклад, припустимо, у нас є функція, яка вітає людей на ім'я, і ми хочемо перевірити, що ім'я, яке ми передаємо, з'являється у вихідній стрічці:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground">pub fn greeting(name: &str) -> String {
format!("Hello {}!", name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn greeting_contains_name() {
let result = greeting("Carol");
assert!(result.contains("Carol"));
}
}
</code></pre>
<p>Вимоги до цієї програми ще не були узгоджені, і ми цілком певні, що текст <code>Hello,</code> на початку вітання зміниться. Ми вирішили не оновлювати тест при змінах вимог, отже замість перевірки на точну рівність значенню, яке повернула функція <code>greeting</code>, ми просто ствердимо, що результат містить текст текст вихідного параметра.</p>
<p>Тепер введімо ваду у цей код, змінивши <code>greeting</code> s виключивши <code>name</code>, щоб побачити, як виглядає тест провалу за замовчуванням:</p>
<pre><code class="language-rust not_desired_behavior noplayground">pub fn greeting(name: &str) -> String {
String::from("Hello!")
}
<span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn greeting_contains_name() {
</span><span class="boring"> let result = greeting("Carol");
</span><span class="boring"> assert!(result.contains("Carol"));
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p>Запуск цього тесту виводить таке:</p>
<pre><code class="language-console">$ cargo test
Compiling greeter v0.1.0 (file:///projects/greeter)
Finished test [unoptimized + debuginfo] target(s) in 0.91s
Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)
running 1 test
test tests::greeting_contains_name ... FAILED
failures:
---- tests::greeting_contains_name stdout ----
thread 'main' panicked at 'assertion failed: result.contains(\"Carol\")', src/lib.rs:12:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::greeting_contains_name
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
</code></pre>
<p>Цей результат вказує на те, що ствердження провалилося і в якому рядку. Більш корисне повідомлення про помилку може вивести значення з функції <code>greeting</code>. Додаймо власне повідомлення про помилку, складене зі стрічки форматування з заповнювачем, заповненим фактичним значенням, яке ми отримали від функції <code>greeting</code>:</p>
<pre><code class="language-rust ignore"><span class="boring">pub fn greeting(name: &str) -> String {
</span><span class="boring"> String::from("Hello!")
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span> #[test]
fn greeting_contains_name() {
let result = greeting("Carol");
assert!(
result.contains("Carol"),
"Greeting did not contain name, value was `{}`",
result
);
}
<span class="boring">}
</span></code></pre>
<p>Тепер, коли ми запустимо тест, ми отримаємо більш інформативне повідомлення про помилку:</p>
<pre><code class="language-console">$ cargo test
Compiling greeter v0.1.0 (file:///projects/greeter)
Finished test [unoptimized + debuginfo] target(s) in 0.93s
Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)
running 1 test
test tests::greeting_contains_name ... FAILED
failures:
---- tests::greeting_contains_name stdout ----
thread 'main' panicked at 'Greeting did not contain name, value was `Hello!`', src/lib.rs:12:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::greeting_contains_name
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
</code></pre>
<p>Ми можемо бачити значення, яке ми фактично отримали на виході тесту, що допоможе нам налагодити, що сталося замість того, що ми очікували.</p>
<h3 id="Перевірка-Наявності-Паніки-з-should_panic"><a class="header" href="#Перевірка-Наявності-Паніки-з-should_panic">Перевірка Наявності Паніки з <code>should_panic</code></a></h3>
<p>На додаток до перевірки значень, повернених з функцій, важливо перевірити, чи наш код обробляє помилкові стани, які ми очікуємо. Наприклад, розгляньте тип <code>Guess</code>, який ми створили у Блоці коду 9-13 у Розділі 9. Інший код, який використовує <code>Guess</code>, залежить від гарантії, що екземпляри <code>Guess</code> будуть містити лише значення у діапазоні від 1 до 100. Ми можемо написати тест, який гарантує, що намагання створити екземпляр <code>Guess</code> зі значенням поза інтервалом панікує.</p>
<p>Ми робимо це, додаючи атрибут <code>should_panic</code> до нашої тестової функції. Тест проходить, якщо код усередині функції панікує; тест провалюється, якщо код усередині функції не запанікував.</p>
<p>Блок коду 11-8 показує тест, який перевіряє, що умови помилки <code>Guess::new</code> стаються тоді, коли ми очікуємо на них.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground">pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess { value }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn greater_than_100() {
Guess::new(200);
}
}
</code></pre>
<p><span class="caption">Блок коду 11-8: Тестування умови, що призводить до <code>panic!</code></span></p>
<p>Ми розміщаємо атрибут <code>#[should_panic]</code> після <code>#[test]</code> перед тестовою функцією, до якої він застосовується. Подивімося на результат, коли цей тест проходить:</p>
<pre><code class="language-console">$ cargo test
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished test [unoptimized + debuginfo] target(s) in 0.58s
Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
running 1 test
test tests::greater_than_100 - should panic ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests guessing_game
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</code></pre>
<p>Здається, усе гаразд! Тепер додамо ваду у наш код, видаливши умову, що функція <code>new</code> запанікує, якщо значення більше за 100:</p>
<pre><code class="language-rust not_desired_behavior noplayground"><span class="boring">pub struct Guess {
</span><span class="boring"> value: i32,
</span><span class="boring">}
</span><span class="boring">
</span>// --snip--
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess { value }
}
}
<span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> #[should_panic]
</span><span class="boring"> fn greater_than_100() {
</span><span class="boring"> Guess::new(200);
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p>Коли ми запустимо тест з Блоку коду 11-8, він провалюється:</p>
<pre><code class="language-console">$ cargo test
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished test [unoptimized + debuginfo] target(s) in 0.62s
Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
running 1 test
test tests::greater_than_100 - should panic ... FAILED
failures:
---- tests::greater_than_100 stdout ----
note: test did not panic as expected
failures:
tests::greater_than_100
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
</code></pre>
<p>В цьому випадку ми отримуємо не дуже помічне повідомлення, але коли подивитися на тестову функцію, то бачимо, що вона анотована <code>#[should_panic]</code>. Отриманий провал означає, що код у тестовій функції не призвів до паніки.</p>
<p>Тести, що використовують <code>should_panic</code>, можуть бути неточними. Тест із <code>should_panic</code> пройде, навіть якщо тест запанікує з іншої причини, а не очікуваної. Щоб зробити тести із <code>should_panic</code> більш точними, ми можемо додати необов'язковий параметр <code>expected</code> до атрибута <code>should_panic</code>. Тестова оболонка забезпечить, щоб повідомлення про провал містило наданий текст. Наприклад, розгляньте модифікований код <code>Guess</code> у Блоці коду 11-9, де функція <code>new</code> панікує з різними повідомленнями залежно від того, значення занадто мале або занадто велике.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Guess {
</span><span class="boring"> value: i32,
</span><span class="boring">}
</span><span class="boring">
</span>// --snip--
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 {
panic!(
"Guess value must be greater than or equal to 1, got {}.",
value
);
} else if value > 100 {
panic!(
"Guess value must be less than or equal to 100, got {}.",
value
);
}
Guess { value }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic(expected = "less than or equal to 100")]
fn greater_than_100() {
Guess::new(200);
}
}
</code></pre>
<p><span class="caption">Блок коду 11-9: тестування <code>panic!</code> з повідомленням паніки, що містить зазначену підстрічку</span></p>
<p>Цей тест пройде, оскільки значення, яке ми додали в параметр <code>expected</code> атрибуту <code>should_panic</code> є підстрічкою повідомлення, з яким панікує функція <code>Guess::new</code>. Ми могли б вказати повне повідомлення паніки, яке очікуємо, яке у цьому випадку буде <code>Guess value must be less than or equal to 100, got 200.</code> Те, що саме ви зазначите, залежить від того, яка частина повідомлення паніки є унікальною чи динамічним і наскільки точним тест ви хочете зробити. У цьому випадку підстрічки повідомлення паніки достатньо, щоб переконатися, що код у тестовій функції обробляє випадок <code>else if value > 100</code>.</p>
<p>Щоб побачити, що трапиться, якщо тест <code>should_panic</code> з повідомленням <code>expected</code> провалюється, знову додамо ваду у наш код, обмінявши тіла блоків <code>if value < 1</code> та <code>else if value > 100</code>:</p>
<pre><code class="language-rust ignore not_desired_behavior"><span class="boring">pub struct Guess {
</span><span class="boring"> value: i32,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Guess {
</span><span class="boring"> pub fn new(value: i32) -> Guess {
</span> if value < 1 {
panic!(
"Guess value must be less than or equal to 100, got {}.",
value
);
} else if value > 100 {
panic!(
"Guess value must be greater than or equal to 1, got {}.",
value
);
}
<span class="boring">
</span><span class="boring"> Guess { value }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> #[should_panic(expected = "less than or equal to 100")]
</span><span class="boring"> fn greater_than_100() {
</span><span class="boring"> Guess::new(200);
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p>Цього разу, коли ми запускаємо тест <code>should_panic</code>, він провалиться:</p>
<pre><code class="language-console">$ cargo test
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished test [unoptimized + debuginfo] target(s) in 0.66s
Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
running 1 test
test tests::greater_than_100 - should panic ... FAILED
failures:
---- tests::greater_than_100 stdout ----
thread 'main' panicked at 'Guess value must be greater than or equal to 1, got 200.', src/lib.rs:13:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: panic did not contain expected string
panic message: `"Guess value must be greater than or equal to 1, got 200."`,
expected substring: `"less than or equal to 100"`
failures:
tests::greater_than_100
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
</code></pre>
<p>Повідомлення про провал вказує на те, що цей тест дійсно панікував, як ми очікували, але повідомлення про паніку не містить очікувану стрічку <code>'Guess value must be less than or equal to 100'</code>. Повідомлення про паніку, яке ми взяли, в цьому випадку було <code>Guess value must be greater than or equal to 1, got 200.</code> Тепер ми можемо почати з'ясуоввати, де знаходиться наша помилка!</p>
<h3 id="Використання-resultt-e-у-Тестах"><a class="header" href="#Використання-resultt-e-у-Тестах">Використання <code>Result<T, E></code> у Тестах</a></h3>
<p>Всі наші тести поки що панікують, коли провалюються. Ми також можемо написати тести, які використовують <code>Result<T, E></code>! Ось тест зі Блоку коду 11-1, переписаний для використання <code>Result<T, E></code>, який повертає <code>Err</code> замість паніки:</p>
<pre><code class="language-rust noplayground">#[cfg(test)]
mod tests {
#[test]
fn it_works() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
Err(String::from("two plus two does not equal four"))
}
}
}
</code></pre>
<p>Функція <code>it_works</code> зараз повертає тип <code>Result<(), String></code>. У тілі функції, замість того, щоб викликати макрос <code>assert_eq!</code>, ми повертаємо <code>Ok(())</code>, коли тест пройшов і <code>Err</code> зі <code>String</code> всередині, коли тест провалено.</p>
<p>Написання тестів, щоб вони повертали <code>Result<T, E></code>, дозволить вам використовувати оператор знак питання у тілі тестів, що може бути зручним способом писати тести, що мають провалитися, якщо будь-яка операція всередині них повертає варіант <code>Err</code>.</p>
<p>Ви не можете використовувати анотацію <code>#[should_panic]</code> в тестах, які використовують <code>Result<T, E></code>. Щоб ствердити, що операція повертає варіант <code>Err</code>, <em>не</em> використовуйте оператор знак питання значенні на <code>Result<T, E></code>. Натомість, використовуйте <code>assert!(value.is_err())</code>.</p>
<p>Тепер, коли ви знаєте кілька способів писати тести, подивімося на те, що відбувається, коли ми запускаємо наші тести і дослідимо різні опції, як ми можемо використовувати з <code>cargo test</code>.
ch08-02-strings.html#concatenation-with-the--operator-or-the-format-macro ch11-02-running-tests.html#controlling-how-tests-are-run</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="ch11-00-testing.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="ch11-02-running-tests.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="ch11-00-testing.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="ch11-02-running-tests.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
</body>
</html>