forked from burakbayramli/books
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchaptesting.tex
951 lines (771 loc) · 41.9 KB
/
chaptesting.tex
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
944
945
946
947
948
949
950
951
\movetooddpage
\chapter{Test Etmek} \label{testing}
\thischapterhas{
\item JUnit ile birim testleri
\item JMeter ile kabul testleri
\item jMock
}
\versal{P}\textsc{rogramlama} sırasında, üzerinde çalıştığımız kodu arada sırada
işletebilmek için kodu derleme işleminden geçirmemiz gerekir. Derleme işlemini
görevlerinden biri işler kod üretmektir, ve çok önemli diğer bir görevi ise tüm
kodlar üzerinde sözdizim kontrolü yapmaktır. Meselâ güçlü tipleme (strong
typing) bekleyen bir dil kullanıyorsak, bir eşitliğin (\PVerb!=!) iki
tarafındaki iki değişkenin tiplerinin birbiri ile aynı olması gerekecektir. Bu,
bize dil tarafından verilen bir kontroldür, ve yapabileceğimiz potansiyel bir
hata için hatırlatıcı nitelik taşır.
Dil tarafından getirilmiş bu şekildeki kurallar, programımızın doğruluğu için
getirilmiş tecrübeye dayalı kuralların toplamıdır. Bu kurallar nasıl
konulmuştur? Eğer istatistiki olarak bir tipteki değişkenin ötekine eşitlenmesi
programcı tarafından çok yapılan bir hata ise, bu hatayı öngören bir kural,
programımızı eşitleme hatalarından kurtarmış olur. Alternatif olarak zayıf tipli
(weakly typed) diller her tipi, her diğer tipe eşitlemeye izin verip,
programcıya sormadan tip değişimini işleyiş anında yaparlar. Bu dillerin
kullanım beklentileri ona göredir.
Derleme aşaması sözdizim hatalarını kontrol eder; Fakat bu kontrollerden geçen
kodumuz, tüm hatalar arınmış mıdır? Bu soruya cevap doğal olarak ``Hayır''
olacaktır. Programcılık ile uğraşan herkesin bildiği gibi, sözdizimsel kurallar
potansiyel hataları bulmakta yardımcı olsalar da, programın içinde mantık
hataları (bug) olması hâla muhtemeldir. Hâtta, yine istatiksel olarak
diyebiliriz ki, çetrefillik seviyesi orta ve üstü seviyede olan hiçbir program
ilk düzgün derlenmesinden sonra beklendiği gibi çalışmayacaktır. Unutkan olan
insanlar (Kural \#4), muhakkak programı yazarken mantık hatası yaparlar.
O zaman programımızı yazarken, aynen sözdizimsel hataları derleyiciye
yakalattırdığımız gibi, mantık hatalarını da yakalayabilecek olan kendi kontrol
edici kurallarımızı kendi üzerimizde yaratmamız gereklidir. Fakat bu
kontrollerin tip kontrolleri gibi sözdizimsel seviyede değil, programın işleyişi
seviyesinde olması gerekecektir.
Program işleyişini kontrol etmek istiyorsak, programımızı {\em işletmemiz} ve
sonucunu kontrol etmemiz gerekecektir. Bu işleyişin önemli faktörlerinden biri
{\em otomatik} yapılabilmesi olmalıdır çünkü otomatik yapılabilen kontroller,
birden fazla ve sıra hâlinde kodun üzerinde işletilebilirler.
Bu tür otomatik işleyiş kontrollerini iki seviyede gerçekleştirmemiz mümkündür:
Birincisi en ufak kod birimi (modül) bazında, ikincisi ise programın geneli,
yâni programın dış kullanılış bazında olacaktır. Bu iki kontrolü yöntemini nasıl
kuracağımızı ve kullanabileceğimizi alttaki bölümlerde göreceğiz.
\section[Birim Testleri][BİRİM TESTLERİ]{Birim Testleri}
Birim testleri bir fonksiyon ya da metot seviyesinde yapılır. Birim testleri
isimlerini, en ufak işler kod birimi olan ``fonksiyonu'' test ediyor
olmalarından alırlar.
Birim testinin kullanımı oldukça basittir: Normal program işleyişi sırasında bir
bir metot ya da fonksiyonu test etmek istiyorsak, o metotu içeren nesneyi
\PVerb!new! ile yaratırız, ve sadece test etmek istediğimiz metotu
çağırırız. Meselâ elimizde iki sayıyı toplayan \PVerb!add! adlı bir metot
olsun.
\begin{lstlisting}[language=Java, frame=none]
public class AdderService
{
public int add(int x, int y) {
return x+y;
}
}
\end{lstlisting}
Bu class'ı test etmek için, bir \PVerb!main! işlevden bu class'ı şöyle
kullanırız (aslında tavsiye ettiğimiz test çağırıcı nokta \PVerb!main!
değildir, fakat şimdilik -kurması çok basit olması sebebiyle- örneğe bu şekilde
başlamak istedik)
\begin{lstlisting}[language=Java, frame=none]
public static void main(String[] args) throws Exception {
AdderService service = new AdderService();
if (service.add(2, 2) != 4) {
System.out.println(``test failed'');
System.exit(-1);
}
}
\end{lstlisting}
Bu testi komut satırından çağırdığımızda, eğer fonksiyon doğru yazılmışsa hiçbir
hata mesajı görmememiz gerekir. Birim testlerinin amacı budur: Belli değerleri
işlem mantığına girdi olarak verince, beklediğimiz çıktı değerlerinin verilip
verilmediğini işlem anında (runtime) kontrol etmek, verilmediyse hata raporu
verebilmek. Üstte verilen fonksiyon ve test çok basit örneklerdir, fakat orta ve
daha üstü çetrefillikte olan bir fonksiyonu da aynı yöntemle test edebiliriz, ve
bu gibi testlerden birçoğunu otomatik olarak işlem kodu üzerinde arka arkaya
işletebiliriz.
\subsection{JUnit - Birim Test İşletici}
Eğer birden fazla birim testi çağırabilmek ve daha iyi karşılaştırıcı
foknsiyonlar kullanmak istiyorsak, JUnit\footnote{http://www.junit.org}
projesinin test altyapısını kullanabiliriz. JUnit,
\begin{itemize}
\item Birim testlerinizi \PVerb!main! yerine, test edici özel class'lar
içinden çağırmamızı sağlar.
\item Özel test edici class'da ismi ``\PVerb!test!'' ile başlayan her metotu,
JUnit otomatik olarak çağırabilir (böylece \PVerb!main! içinden (elle) her
çağrıyı eklememiz gerekmez)
\item \PVerb!assertEquals!, \PVerb!assertTrue! gibi karşılaştırıcı
fonksiyonlar sağlayarak, basmakalıp \PVerb!if! çağrılarından bizi kurtarır
\end{itemize}
Bu altyapının üstüne, tarafımızdan, ``sonu \PVerb!Test.class! ile biten'' her
class'ın da birim test class'i kabul edilip, işletilmesini sağlayan
\PVerb!AllTest! adlı bir global işletici yazılmıştır. Bu global işletici, sonu
\PVerb!Test.class! ile biten tüm dosyaları \PVerb!CLASSPATH!'ten toplayarak
JUnit'i çağırabilir ve birim testlerin işletilmesini ve sonuçların toplanmasını
sağlayabilir. \PVerb!AllTest! yardımcı kodu Ant \PVerb!build.xml! içinden
çağırılabilir. Örnek kitap kodlarımızdaki her proje içindeki
\PVerb!build.xml! içinde bu tür bir \PVerb!test! target'i
bulabilirsiniz. Kullanım şöyledir:
\begin{lstlisting}[language=Java, frame=none]
...
<target name="test" depends="clean,compile">
<java fork="yes" classname="org.mycompany.kitapdemo.util.AllTest"
taskname="junit" failonerror="true">
<arg value="Test.class"/>
<classpath refid="compile.classpath"/>
</java>
</target>
...
\end{lstlisting}
Bu \PVerb!build.xml! bloğuna göre, önce \PVerb!clean! ve \PVerb!compile!
target'leri işletilerek eski işler kodlar silinip kaynak kodlar derlenecek,
sonra \PVerb!AllTest! class'ı, \PVerb!Test.class! parametresi ile
çağırılacaktır.
\PVerb!Test.class! yerine değişik bir sonekle biten birim test
class'larını çağırmak istiyorsaniz, \PVerb!<arg value ..>! için değişik bir
değer verebilirsiniz. Şimdi örnek bir JUnit birim testini görebiliriz.
\begin{lstlisting}[language=Java, frame=none]
import junit.framework.TestCase;
public class AdderServiceTest extends TestCase {
public void testAdd() {
AdderService service = new AdderService();
assertEquals(5, service.add(3, 2));
}
}
\end{lstlisting}
Testleri işletmek için komut satırında \PVerb!ant test! yazmanız
yeterlidir. Bunun sonuc olarak şöyle bir çıktı göreceksiniz:
\begin{lstlisting}[language=Java, frame=none]
...
...
test:
Adding org.mycompany.kitapdemo.sample.AdderServiceTest
Adding org.mycompany.kitapdemo.util.AllTest\$Test
...
Time: 3.835
OK (3 tests)
\end{lstlisting}
Testlerimiz işlemiş, kodların doğru işlediğini bulmuş ve sonucu
bildirmiştir. Eğer kodlarda bir yanlışlık olsaydı (meselâ \PVerb!+! işareti
yerine yanlışlıkla bir \PVerb!-! işareti konmuş olsa),
\begin{lstlisting}[language=Java, frame=none]
public class AdderService {
public int add(int x, int y) {
return x +- y; // yanlış kod!
}
}
\end{lstlisting}
o zaman test şöyle bir hata yakalayacaktı:
\begin{lstlisting}[language=Java, frame=none]
..
..
There was 1 failure:
1) testAdd(org.mycompany.kitapdemo.sample.AdderServiceTest)junit.fram
ework.AssertionFailedError: expected:<5> but was:<1>
at org.mycompany.kitapdemo.sample.AdderServiceTest.testAdd(Ad
derServiceTest.java:30)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAcc
essorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingM
ethodAccessorImpl.java:25)
at org.mycompany.kitapdemo.util.AllTest.main(AllTest.java:86)
FAILURES!!!
Tests run: 4, Failures: 1, Errors: 0
...
\end{lstlisting}
Sonuca bakarsak, \PVerb!expected:<5> but was:<1>! mesajının verildiğini
görüyoruz, yâni JUnit bize birim testimizin sonuç olarak 5 beklediğini, ama test
edilen birimden 1 değerinin geldiğini söylemektedir. Bu hatalı bir durumdur,
beklenen ve gelen değeri karşılaştıran JUnit, bir uyumsuzluk olduğunu görünce,
hatalı bir kod olduğunu anlamıştır, ve çok detaylı bir tarif vermiştir. Bu
detaylı târif \PVerb!assertEquals! kullanımı sayesinde gelmiştir. Hatanın sebebi
ise, yanlışlıkla koda koyulmuş \PVerb!+-! işleminin toplama değil, çıkartma
yapmasıdır!
\subsection{Kurumsal Kodları Birim Testinden Geçirmek}
\PVerb!AdderService.add()! islemi, dışarıdan oldukça izole bir işlem kodudur.
Bu metot üzerinde test gerçekleştirmek bu sebeble oldukça kolaydı. Fakat bir
kurumsal uygulamalanın işlemesi için yazılan kodların, genellikle bir {\em
birleştirici} (integrator) özelliği vardır; Sektörümüzde, işi sadece kurumsal
uygulama yazmak olan şirketlere bu sebeple ``sistem birleştiricisi (system
integrator)'' ismi verilir. Kurumsal bir uygulama, çok nadiren bir ada olarak
tek başına iş yapmaktadır. Ya bir veri tabanına bağlanırız, ya bir e-mail server
üzerinde e-mail atarız, ya da eski (legacy) bir sisteme bağlanarak bilgi
alışverişi yapmamız gerekir.
Bu iletişim gereklilikleri, doğal olarak işlem mantığı kodlarımızın içine kadar
nüfuz eder. Bunun kod bakımı açısından bir sakıncası yoktur. Sadece test
ederken, dış bir sistemle iletişim gerekliliklerinden dolayı birim testinden
geçirmek için çağırdığınız kod parçacıkları, bir birim test ortamı içinde
işlediklerinden habersiz {\em o dış sisteme} bağlanabilmeye çalışacaklardır. Ve
bunda başarısız olacaklardır, çünkü sonuçta, içinde işlem yaptığımız ortam bir
{\em test} ortamıdır. Gerekli olan tüm dış servisler büyük bir ihtimalle daha
başlatılmamıştır, ve zaten başlatılmalarının da beklenmemesi gerekir: Bir birim
testinden beklediğimiz, komut satırından, hiçbir dış sisteme ihtiyaç duymadan
programın çetrefil mantığını (onu çagır bunu çağır mantığını değil) test
edip bize sonucu bildirmesidir.
O zaman kurumsal uygulamaları nasıl edeceğiz? Kurumsal uygulamalar bir ada
değildir, ama birim testler uygulamanın bir ada olmasını ister. O zaman ada
olmayan kodları ada hâline getirmenin tekniklerini öğrenmemiz gerekiyor.
\subsubsection{Bir Nesneyi Taklit Etmek}
Taklit etmek (mocking), kurumsal uygulamaları birim testinden geçirebilmek için
ihtiyacımız olan tekniktir. Bir birim testinin çağırdığı işlem kodlarının {\em
bağlanmak istediği} dış sistemin yerine taklitini koyarsak ve o taklit nesneye
istediğimiz (o test senaryosu için gereken) verileri verdirebilirsek, işlem
kodlarımız hiçbir şeyden habersiz işleyip geri dönebileceklerdir. Birim testleri
de böylece istedikleri işlem mantığını test edebilmiş olacaklardır. Yâni birim
testlerinin işlemesi için, dış sistem nesneleri, taklit edilmelidir.
Taklitlemeyi, her dış sistem için değişik bir şekilde yapacağız (taklitlemeden
muhaf tutacağımız tek dış sistem veri tabanı olacak, bunun sebeplerini
\ref{hibernate:test:whichdb} bölümünde okuyabilirsiniz).
\begin{itemize}
\item \textbf{Socket, vs gibi dış sistemlere ve ya API'ları çâğıran kodlar
için}: jMock aracılığı ile bir nesnenin yerine metotlarının içi
boşaltılmış ve sadece bizim istediğimiz cevapları döndüren bir Java taklit
nesnesi koymak mümkündür.
\item \textbf{Diğer}: Bu tür şartlar daha çetrefil taklitleme
gerektirebilir, ve elle kodlama isteyebilirler. O zaman, dış sisteme
bağlanan o nesneden miras alıp (\PVerb!extend!), metotlarının içine
testimizin beklediği cevapları verecek kodlar koyarız. Bu, jMock
kütüphanesinin otomatik, "perde arkasında" yaptığının "perde önünde" olan
karşılığıdır.
\end{itemize}
jMock ile istediğimiz herhangi bir nesnenin yerine onun taklidini koyabilir, ve
bu taklidin metotlarına "dinamik olarak" istediğimiz cevapları
verdirebiliriz. jMock, arka planda CGLIB adında baytkod üretmeyi sağlayan bir
araç kullanmaktadır. CGLIB sayesinde jMock, meselâ dönüş değeri bir
\PVerb!String! olarak tanımlanan metot ismi ve onun "beklediğimiz dönüş
değerlerini" dinamik olarak, sanki esas taklit edilen nesneden geliyormuş gibi,
işlem anında üretebilmektedir.
jMock kullanımını bir örnek üzerinde görelim. Alttaki kodlar içinde
\PVerb!SocketClient! adlı bir nesne görüyoruz. Bu nesne, İnternet'teki bir
makina ve o makinadaki port'a bir Socket açmaktadır. Bu makinanın nerede
olduğuna dikkat edelim. Evet, Japonya'da! Şimdi, bu \PVerb!SocketClient!'ı
kullanan işlem mantığı koduna gelelim. Bu class'ın ismi de (uygun olarak)
\PVerb!AppLogic!, ve yaptığı da \PVerb!SocketClient! üzerinden Japonya'ya
bağlanıp oradan aldığı Japonca bilgileri Türkçe'ye tercüme etmek.
\begin{lstlisting}[language=Java, frame=none]
public class SocketClient {
public String takeWordFromJapan(){
String inputLine = "";
try{
Socket socket = new Socket("www.japan.jp", 5555);
PrintWriter out = new PrintWriter(socket.getOutputStream(),
true);
BufferedReader in = new BufferedReader(new InputStreamReader
(socket.getInputStream()));
out.println("DOMO ARIGATO!!! ");
while ((inputLine = in.readLine()) != null) {
out.println(inputLine);
}
out.close();
in.close();
socket.close();
} catch(IOException e){
e.printStackTrace();
}
return inputLine;
}
}
\end{lstlisting}
\begin{lstlisting}[language=Java, frame=none]
public class AppLogic {
SocketClient client = new SocketClient();
public void setSocketClient(SocketClient client) {
this.client = client;
}
public String translate() {
String message = client.takeWordFromJapan() ;
if (message.equals("HAI")) {
return "evet";
} else if (message.equals("DOZO")) {
return "lutfen";
} else if (message.equals("CAMPARE")) {
return "??";
}
return "";
}
}
\end{lstlisting}
Bizim amacımız, \PVerb!AppLogic! nesnesini test etmektir. O zaman
\PVerb!AppLogic! kodlarını dışarıdan izole olmuş bir ada hâline getirmemiz
gerekiyor. \PVerb!AppLogic! kodlarına bakarsak, test ettiğimiz mantığın
\PVerb!translate! metotu içindeki \PVerb!if! komutunun olduğunu görüyoruz.
Demek ki, \PVerb!SocketClient! yerine bir taklidini koyarsak, kodlarımızı dış
dünyadan izole etmiş olacağız, ve bu taklide testimiz içinde istediğimiz Japonca
kelimeyi "söylettirebilirsek", bu senaryonun gerektirdiği \PVerb!translate!
metotunu test edebilmiş olacağız. İşte jMock burada sahneye giriyor. jMock
kullanan bir test aşağıdaki gibi olacaktır.
\begin{lstlisting}[language=Java, frame=none, numbers=left,numberstyle=\tiny]
public class TranslateTest extends MockObjectTestCase {
public void testHai() {
Mock mockObj = mock(SocketClient.class);
AppLogic logic = new AppLogic();
logic.setSocketClient((SocketClient)mockObj.proxy());
mockObj.expects(atLeastOnce()).
method("takeWordFromJapan")
.will(returnValue("HAI"));
String answer = logic.translate();
assertEquals("evet", answer);
}
}
\end{lstlisting}
\#5'te \PVerb!SocketClient! nesnesinin taklit tanımını yapıyoruz. \#7'de test
edeceğimiz \PVerb!AppLogic! nesnesini yaratıyoruz, ve taklit nesnesinin kendisi
\#9'da yaratıp \PVerb!AppLogic! üzerinde set ediyoruz. Bu numaraya dikkat! Eğer
\PVerb!AppLogic! ihtiyacı olan \PVerb!SocketClient! nesnesini \PVerb!translate!
metotu {\em içinde kendisi} yaratıyor olsaydı, biz bu şekilde taklide dayalı bir
testi yapamazdık. Çünkü işlem kodları bizim verdiğimiz taklit dış nesnesi
yerine, kendi yarattığı ve Japonya'ya bağlanmaya çalışacak esas nesneyi
kullanırdı. O zaman, işlem kodlarımızın dış nesneleri her zaman dısarıdan, bir
set aracılığı ile alması gerekmektedir.
Taklit nesnesine belli cevapları verdirmek için ise, jMock'un \PVerb!expects!,
\PVerb!method! \PVerb!returnValue! komutlarını kullanacağız. Bu özellikler jMock
teknolojisinin en güçlü tarafını teşkil etmektedir, dinamik, \PVerb!String! ile
tanımladığımız metot isimleri ve dönüş değerlerini arka planda baytkod hâline
gelip, JVM tarafından anında işleme konulmaktadır! Üstteki örnekte,
\#11. satırda taklit nesnesine beklenen (ve taklit edilmiş) çağrının ``en az bir
kere geleceğini'' tanımlamış oluyoruz. Yâni \PVerb!expects(atLeastOnce())!
kullanımı sayesinde, taklit edilmiş nesneye ``kaç kere çağrı yapılıyor
olmasını'' bile test senaryomuza dahil etmemiz mümkün olmaktadır. Bu da
önemlidir, çünkü eğer işlem kodları biz dış nesne çağrılması beklerken hiç çağrı
yapmazsa, bu da bir hata durumu olacaktır ve bu durumun yakalanması kodumuzun
doğruluğu açısından faydalı olabilir.
Taklit nesne üzerinde çağırılacak metodun ismini \#12'de tanımlıyoruz. \#13,
dönüş değerini tanımlıyor. Bu hazırlıklardan sonra, artık \PVerb!AppLogic!'i
çağırarak, teste gelen sonucu kontrol edebiliriz.
\#15'te yapılan \PVerb!translate! çağrısı, \PVerb!AppLogic!'den taklit
\PVerb!SocketClient!'a yapılan bir \PVerb!takeWordFromJapan! çağrısına sebebiyet
verecektir. Biz de zaten bu senaryoyu planlamıştık ve \#11'deki
\PVerb!expects(atLeastOnce())! tanımının sebebi buydu. Beklenen bu çağrının
cevabını da pişirip taklit nesneye hazırlatmıştık, cevap ``HAI'' kelimesi
olacaktı.
Bu cevap gelince, sıra (esas test ettiğimiz) \PVerb!AppLogic! class'ına gelir,
\PVerb!translate! kodunun geri kalanı işler. ``HAI'' görünce ``evet'' Türkçe
cevabı verilmesi gerektiğine, \PVerb!if! kodları karar verecektir (eğer doğru
yazılmışlarsa). Eğer hakikaten geriye ``evet'' cevabı döndürülürse, bu cevabı
kontrol eden JUnit testimiz, başarıyla senaryonun geçtiğini rapor edecektir.
Üstte gösterilen örnek kodların tümünü \PVerb!JUnitSample! projesi altında
bulabilirsiniz. Birim testleri işletmek için \PVerb!ant test! komutunu
kullanınız.
\subsubsection{Kendi Kodumuz ile Taklit Etmek}
Eğer jMock ile taklitleme tekniğini kullanmak istemezsek, taklit nesnesini
kendimiz de yaratabiliriz. Bunun için yapmamız gereken, taklit edilen nesneden
miras alıp, taklit edilen metotu tekrar tanımlamaktır (redefine). Meselâ
\PVerb!SocketClient! class'ının taklitini elle şöyle yazabiliriz.
\begin{lstlisting}[language=Java, frame=none]
public class SocketClientManualMock extends SocketClient {
private String returnThis;
public void setReturnWhichWord(String word) {
this.returnThis = word;
}
// tekrar tanım
public String takeWordFromJapan(){
return returnThis;
}
}
\end{lstlisting}
Bu nesnede, görüldüğü gibi, hiçbir socket işlemi yapılmıyor. Japonya'dan
\PVerb!String! alması gereken metot, sadece, daha önceden set edilmiş bir
\PVerb!String! değerini döndürmek üzere ayarlanmıştır. Bu düzen, jMock
şartlarında gördüğümüz \PVerb!will(returnValue(..))! kullanımına
eşdeğerdir. Testin kendisi de şöyle gözükecektir.
\begin{lstlisting}[language=Java, frame=none]
public class TranslateManualMockTest extends TestCase {
public void testHai() {
SocketClientManualMock mockObj = new SocketClientManualMock();
AppLogic logic = new AppLogic();
logic.setSocketClient(mockObj);
mockObj.setReturnWhichWord("HAI");
String answer = logic.translate();
assertEquals("evet", answer);
}
}
\end{lstlisting}
Bu testin de işleyip başarıyla geri döndüğünü göreceğiz.
Bu yöntemin, jMock yöntemine nazaran dezavantajı fazladan bir class yazılmasını
macbur bırakmasıdır. Bu da kod bazımızda class enflasyonuna yol açabilir. jMock
ile dış class dinamik olarak üretilmiştir, ve hiçbir ek class'a gerek
yoktur. İki yöntem arasında seçim yaparken tavsiyemiz, taklitlemeyi önce jMock
ile başlamak eğer bir şekilde takılınır ve çok çetrefil bir ortamda jMock
kullanımı zorlamaya başlarsa, elle taklitleme yapılmaya başlanmasıdır.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Hibernate Test Altyapısı}
\label{testing:unit:hibernate}
\PVerb!SimpleHibernate! örnek projesinde Hibernate üzerinden veri tabanına
erişen birçok senaryoyu görmeniz mümkündür. \PVerb!SimpleHibernate! JUnit test
kodlarını çalıştırmak için, komut satırından \PVerb!ant test! komutunu vermeniz
yeterlidir. Bu bölümde, Hibernate kodlarını test edebilmek için faydalı bir
class \PVerb!org/mycompany/util/TestUtil! kodlarını göreceğiz. \PVerb!TestUtil!
ile, bir test senaryosu için gereken test verisini yüklememiz mümkün olacak.
\subsubsection{Test Verisi}
Hibernate (ya da başka tekniklerle) veri erişimi yapan kodları test etmenin en
zor tarafı test başlamadan önce veri tabanında belli bir test verisinin olması
zorunluluğudur. Bu test verilerin yüklenmesini tabii ki yine Hibernate'i
kullanarak ekleme, güncelleme komutları üzerinden yapmak mümkündür, fakat, test
etmeye uğraştığımız zaten Hibernate veriye erişim kodlarının kendisi değil
midir? Test edilen kodları teste hazırlık için kullanmak garip bir tavuk/yumurta
yumurta/tavuk ilişkisi ortaya çıkarmaktadır. Bu sebeple test verisi yüklemek
için pür SQL kullanarak (SQL komutları içeren bir dosyadan) veri yüklemek ideal
yöntem olacaktır. Zaten test için gereken veri bazen ``mevcut bir tabandan''
``SQL formatında'' geliyor olabilir. O zaman bu veriyi bir \PVerb!.sql! dosyası
üzerinden kullanabilen bir test altyapısı bizim için faydalı olacak.
\PVerb!TestUtil! kodları birim testi başlar başlamaz sıfırdan şema kurmak ve
veri yükleme işlemleri için tarafımızdan yazılmış bir class'tır. Hibernate
kodlarını test etmek için gereken veri yükleme işlemini
\PVerb!TestUtil.createFromFile! ve \PVerb!TestUtil.insertFromFile! metotları
ile yapabilirsiniz.
Yâni veri tabanında şema yaratımı ve veri yüklemesi için bize iki \PVerb!SQL!
dosyası gerekiyor. Bu dosyalardan biri şema üretmemizi sağlayacak \PVerb!CREATE
TABLE! komutları içeren dosya olmalıdır, ikincisi, \PVerb!INSERT! içeren veri
yükleme dosyası olmalıdır. Bu iki dosyanın bir örneğini aşağıda görüyoruz.
\begin{lstlisting}[language=SQL,caption=tables\_mysql.sql]
DROP TABLE IF EXISTS car;
CREATE TABLE car (
license_plate varchar(30) default '',
description varchar(30) default ''
) TYPE=MyISAM;
\end{lstlisting}
\begin{lstlisting}[language=SQL,caption=sample\_data.sql]
truncate table car;
insert into car(license_plate, description)
values('34 TTD 2202','ornek description');
\end{lstlisting}
Bu dosyaları taban üzerinde işletmek için \PVerb!TestUtil!'in iki çağrısını
sırasıyla \PVerb!tables_mysql.sql! ve \PVerb!sample_data.sql! üzerinde
kullanıyoruz. \PVerb!TestUtil!, bu dosyaların içeriğini
\PVerb!hibernate.cfg.xml! içinde belirtilmiş tabanda işletecektir. Böylece
tabana istediğimiz örnek veri yüklenmiş olacaktır.
\begin{lstlisting}[language=Java,caption=SimpleCarTest.java]
public class SimpleCarTest extends TestCase {
public SimpleCarTest() { }
public void testCar() throws Exception {
Connection c = TestUtil.createTestConnection();
TestUtil.createFromFile("tables_mysql.sql", c);
TestUtil.insertFromFile("sample_data.sql", c);
try {
Session s = HibernateSession.openSession();
HibernateSession.beginTransaction();
Car car = (Car) s.get(Car.class, "34 TTD 2202");
assertEquals("ornek description", car.getDescription());
HibernateSession.commitTransaction();
} finally {
HibernateSession.closeSession();
}
}
}
\end{lstlisting}
\PVerb!SimpleCarTest! çok basit bir testi gerçekleştirmektedir. Test başında
\PVerb!SQL! dosyalarının belli verileri yüklediğini bildiğimiz için testin tek
yapması gereken, Hibernate ile veriye erişimi test etmektir. Örnekte \PVerb!Car!
eşlemesi üzerinden Hibernate \PVerb!get!'i test etmiş oluyoruz. Hibernate
\PVerb!get!, kendisine verilen ID üzerinden tek bir nesneyi yüklememizi sağlayan
bir çağrıdır. \PVerb!Car! eşlemesindeki \PVerb!licensePlate! öğesi kimlik olduğu
için \PVerb!get!'e (daha önce test verisini yüklemiş olduğumuz) bir
\PVerb!licensePlate! verince, geriye tek bir nesne gelmesini bekleriz. JUnit
birim testi de aynen bunu yapmaktadır. Kontrol amaçlı olarak biraz önce
okuduğumuz nesne üzerinde \PVerb!description! olarak \PVerb!`örnek description'!
metnini bulmayı amaçlıyoruz (çünkü örnek veride böyledir), ve bu şartı
\PVerb!assertEquals! ile kontrol ediyoruz.
\PVerb!TestUtil! kullanan JUnit testleri hakkında bilmemiz gereken diğer önemli
noktalar şunlardır:
\begin{itemize}
\item \PVerb!TestUtil!'in \PVerb!TestUtil.createFromFile! ve
\PVerb!TestUtil.insertFromFile! metotlarına geçilen her \PVerb!sql! dosya,
\PVerb!CLASSPATH! belirtilen bir dizinde mevcut olmalıdır. \PVerb!TestUtil!,
kendisine işletilmesi için verilen \PVerb!sql! dosyalarını CLASSPATH altında
aramak üzere programlanmıştır. \PVerb!SimpleHibernate! ve diğer tüm kitap
örnek kodlarında \PVerb!src/sql! dizinini biz CLASSPATH'e ekledik.
\item Her test başında \PVerb!TestUtil.createFromFile! ile şemayı sıfırdan
yaratmamız çok önemlidir, çünkü kodlama süreci içinde şemamız başkası
tarafından değiştirilmiş olabilir. Kodlar ve şema bir bütün olduğu için, en
son kod, en son şema ile çalıştırılmalıdır. Bu sebeple her test başında şemayı
sıfırdan yaratarak, kod ve şema uyumsuzluklarını birim test seviyesinde
yakalayabilmiş oluyoruz. Kural \#3 bağlamında, basit bir alışkanlık edinerek
(her test başında şema tekrar yaratılır), şema ve kod arasındaki
uyumsuzlukları erkenden yakalama şansına kavuşuyoruz. Emin olun ki bu
uyumsuzlukların kod içerisinde haftalarca kalıp büyümesi kod kalitesi
açısından hiç iyi olmayacaktır!
\item Birim test felsefesine göre her birim testi kendi kendine yeter bir
şekilde yazılmalıdır, ve bir birim testinin sonucu diğer birim testini
etkilememelidir (her birim testi başında şemayı sıfırdan kurmak için bir sebep
daha, çünkü bir test sonucunu bir ötekini veri tabanı üzerinden bile
etkilememelidir).
\end{itemize}
\subsubsection{Hibernate Birim Testleri Hangi Tabana Bağlanıyor?} \label{hibernate:test:whichdb}
Örnek projelerimizdeki birim testleri, testlerin işlediği makinadan erişilebilen
bir veri tabanının varlığını şart koşmaktadır. Fakat gereksinimler bundan
ibarettir. Meselâ testlerimizin çalışması için tabanda hiçbir şemanın kurulmuş
olması gerekmez. \PVerb!TestUtil! aracılığı ile şemayı sıfırdan kurmak JUnit
birim testleri içinden otomatik olarak gerçekleştirilecektir.
\begin{quote}
\textbf{Not}: \PVerb!TestUtil! bu bağlamda uzun bir değişim sürecinden geçti. Kodun daha
önceki versiyonları, birim testleri işleten kişinin bilgisayarında bir veri
tabanının olamayacağını ihtimale katarak, SQL şema script'lerini MySQL ya da
Oracle formatından HSQLDB formatına anında çevirmeye uğraşıyordu. HSQLDB, gömülü
(embedded) bir taban olduğu için, kullanmak için bir servis başlatmaya gerek
bırakmaz, bu sayede JUnit tarafından rahatlıkla hafızada başlatılarak, yüklenip
testler için hazır hâle getirilebilir. Bu yöntemi takip etmekte amaç, test
işletici üzerinde en az külfeti getirmek idi.
Fakat, bu yöntemin işlemesinde problemler bulunmuştur;
\begin{enumerate}
\item HSQLDB, bazı çetrefil Hibernate sorgularını işletmekte problemler çıkarmış
\item Oracle ya da MySQL şema script'lerinden HSQLDB script'lerine otomatik
olarak çevirim yapan Perl script'lerinin bakımı ve kodlaması fazla zaman alıcı
bir eylem hâline gelmiştir.
\end{enumerate}
Bu sebeple, Kural \#3 ışığında birim testlerini işletmek isteyen her
programcının `ciddi bir veri taban ürününe erişiminin olması' prensibini takip
etmek uygun gözükmüştür.
\end{quote}
\subsubsection{Değişik Test Senaryoları}
Eğer birim testlerinizde değişik senaryoları test etmek isterseniz, bu, çoğu
zaman {\em değişik örnek veri} kullanmak anlamına gelecektir. Bu yeni
senaryolarda (ve JUnit testlerinde), şemayı üreten satır önceki örneklerimizdeki
gibi kalacak, fakat {\em değişik} bir örnek veri dosyasını işleten ve {\em
değişik} değerleri \PVerb!assertEquals! ile kontrol eden bir kod
olacaktır. Meselâ, bu şekilde bir yeni testi altta görelim:
\begin{lstlisting}[language=Java,caption=SimpleCarTestManyRows.java]
public class SimpleCarTestManyRows extends TestCase {
public SimpleCarTest() { }
...
public void testCarNew() throws Exception {
Connection c = TestUtil.createTestConnection();
TestUtil.createFromFile("tables_mysql.sql", c);
TestUtil.insertFromFile("sample_data_2.sql", c);
try {
Session s = HibernateSession.openSession();
HibernateSession.beginTransaction();
Car car = (Car) s.get(Car.class, "52 TT 30");
assertEquals("description 4", car.getDescription());
HibernateSession.commitTransaction();
} finally {
HibernateSession.closeSession();
}
}
}
\end{lstlisting}
Yeni testimizin değişik veriye ihtiyacı var. Bu veriyi, alttaki dosyadan
sağlayabiliriz.
\begin{lstlisting}[language=SQL,caption=sample\_data\_2.sql]
truncate table car;
insert into car(license_plate, description)
values('34 TTD 2202',description 1');
insert into car(license_plate, description)
values('14 TF 399','description 2');
insert into car(license_plate, description)
values('34 RF 493','description 3');
insert into car(license_plate, description)
values('52 TT 30','description 4');
\end{lstlisting}
Görüldüğü gibi yeni testte yüklenen veri \PVerb!sample_data.sql! değil,
\PVerb!sample_data_2.sql! adlı dosyadır, çünkü testimiz için yaratmamız gereken
senaryo bunu gerektiriyordu. Yeni veri üzerinden testin kontrol edeceği araba
nesnesi, \PVerb!52 TT 30! no'lu plakayı taşıyan arabadır. Test, \PVerb!get!
komutu ile bu nesneyi yükler \PVerb!assertEquals! ile sonucu kontrol eder.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section[Kabul Testleri][KABUL TESTLERİ]{Kabul Testleri} \label{testing:acceptance}
Birim testleri en ufak modül (metot) bazında test yapıyorsa, kabul testleri de
(acceptance test) programın dışından, birden fazla modüle dokunabilecek şekilde
bir kullanıcı gözünden test etmemize yarar. Kabul testlerine sektörde
fonksiyonel (functional) test, ya da entegrasyon (integration) testleri isimleri
de verilmektedir. Kabul testleri mümkün olduğu kadar dış sistemlerle
entegrasyonu yapılmış bir sistem üzerinde gerçekleştirilmelidir.
Kabul testlerini en basit şekilde elle (uygulamayı bizzat kullanarak) yapmak
mümkündür. Uygulamamız test edilmeye hazır hâle gelince kodlar test makinasına
gönderilir, ve bir test kullanıcı(lar) belli test senaryolarını uygulama
üzerinde işletip, uygulama için kabul testlerinden geçti ya da kaldı raporu
verebilirler. Test kullanıcısı kabul testlerini gerçekleştirirken elinde belli
test senaryoları olur. Kullanıcı bu senaryoları sırasıyla program üzerinde
işletir sonuçlarını kontrol eder, ve bir yere not eder. Hatalı gözüken sonuçlar,
hata takip sistemine (bug tracking system) girilir, ve düzeltme süreci başlamış
olur.
\subsection{Otomatik Kabul Testleri}
Elle yapılan testler için zaman ve insan gücü harcanması gerekir. Fakat yapılan
işin oldukça mekanik olması sebebiyle otomatik yapılan kabul testleri de artık
tercih edilmeye başlanmıştır (Kural \#7). Kurumsal Web ortamında otomatik kabul
testleri işletmek için birçok ürün mevcuttur; Bizim tavsiyemiz,
\ref{perf:jmeter} bölümünde işlediğimiz ve yük testleri için kullanılan JMeter
ürününü {\em kabul testleri için} kullanmaktır.
JMeter'i bu şekilde iki amaçlı kullanabilmemizin sebebi, bu ürününün Web
uygulamasından gelen cevapları işleyebilen bir birim sağlamasıdır;
Kullanacağımız bu birim, \PVerb!Reponse Assertion! adlı birimdir.
\subsubsection{JMeter Kullanımı}
Bir kabul testinin yük testinden en önemli farkı, kabul testi için bağlanan tek
Thread'in (sanal kullanıcı olarak) yeterli olmasıdır, çünkü test edilen arka
plan kodlarının ölçeklenebilmesi değil, doğru çalışıp çalışmadığıdır. Şekil
\ref{testing:accept:thread} üzerinde gereken Thread ayarlarını görüyoruz.
\begin{figure}[!hbp]
\center{
\scalebox{0.55}{
\includegraphics{./images/accept_test_3.eps}
}
}
\caption{\label{testing:accept:thread} Kabul Test için JMeter Thread Sayısı}
\end{figure}
Geri kalan ayarlar, \ref{perf:jmeter} bölümünden tanıdık olacaktır. JMeter sanal
kullanıcısının bir dinamik sayfaya bağlanıp form'a test değerleri girmesi için
\PVerb!Http Request! biriminin eklenmesi ve gerekli parametrelerin bu birime
verilmesi yeterlidir. Örnek için \PVerb!StrutsHibPerformance! projesinin
\PVerb!resources/acceptance-test-add.jmx! dosyasında içinde tanımlanmış
\PVerb!/kitapdemo/add-car.do! birimine bakabiliriz. Bu örnek kabul testini
\PVerb!StrutsHibPerformance! projesi üzerinde kullanabiliriz.
Örnek test dosyasını, JMeter menüsü \PVerb!File | Open! ile açabilirsiniz. Şekil
\ref{testing:accept:add} üzerinde kabul testin tüm birimlerini, Şekil
\ref{testing:accept:add:detail} üzerinde ise \PVerb!/kitapdemo/add-car.do!
biriminin detaylarını görüyoruz.
\begin{figure}[!hbp]
\center{
\scalebox{0.55}{
\includegraphics{./images/accept_test_1.eps}
}
}
\caption{\label{testing:accept:add} Örnek bir Kabul Testi}
\end{figure}
\begin{figure}[!hbp]
\center{
\scalebox{0.55}{
\includegraphics{./images/accept_test_8.eps}
}
}
\caption{\label{testing:accept:add:detail} Araba Eklemek}
\end{figure}
Bir kabul testinin yapması gereken doğruluk kontrollerini JMeter'ın
\PVerb!Response Assertion! birimini kullanarak yapabiliriz. Bu kontrol, JUnit
birim testlerinden kullandığımız \PVerb!assert! metotları ile eşdeğer bir
özelliktir; Amacımız, testin beklediği cevap değerleri uygulamadan geri
gelmezse, bu kontrol edici birim ile bu hatayı yakalayıp bize bildirmesini
sağlamaktır.
\PVerb!Response Assertion! birimi, yapılan bir Web isteğinden sonra geri gelen
cevap (response) içindeki değerlere bakabilme yeteneğine sahiptir. Bu cevap
içeriği, bir Web sayfasını tarayıcınız ile ziyaret edince tarayıcınıza
gönderilen içerik ile aynıdır. Tarayıcıya gelen HTML içeriği görmek için
Mozilla'da \PVerb!View | Page Source! seçeneğini kullanılabiliriz.
\PVerb!Response Assertion! birimininin kontrolünü yapabilmesi için, hangi Web
isteğinin içeriğine bakacağını bilmesi gerekiyor. Yâni \PVerb!Response
Assertion! her zaman bir ``\PVerb!Http Request!'e'' {\em alt birim} olarak
eklenmelidir. O zaman \PVerb!Response Assertion! eklemek için, bir \PVerb!Http
Request!'e tıklayıp, mouse sağ tıklama ile \PVerb!Add | Assertions | Response
Assertion! seçeneğini seçmeliyiz. Eğer kontrol birimini yanlış yere eklediysek,
bu birimi sürükle/bırak ile gereken \PVerb!Http Request! üzerine götürüp,
\PVerb!Add As Child! seçeneğini kullanmalıyız. Alt birim olarak eklenmiş
\PVerb!Response Assertion! birimini Şekil \ref{testing:accept:add} üzerinde
görüyoruz. Örneğimizde kullanılan \PVerb!Response Assertion! içeriğini Şekil
\ref{testing:accept:assert:detail} üzerinde görülmektedir.
\begin{figure}[!hbp]
\center{
\scalebox{0.55}{
\includegraphics{./images/accept_test_2.eps}
}
}
\caption{\label{testing:accept:assert:detail} Araba Kontrolü Yapan Response Assertion}
\end{figure}
Bu cevap kontrolünde, cevap metni içinde aranacak değerler ``34 TT 2000'' ve
``benim arabam'' kelimeleridir. Bu kelimelerin orada olması lâzımdır, çünkü
\PVerb!add-car.do! Struts Action'ını işini bitirdikten sonra yönlendirmeyi
\PVerb!main.do!'ya yapar. \PVerb!main.do! ise, o anda sistemde olan tüm arabaları
listelemek ile yükümlüdür, ve biz de daha önceden \PVerb!add-car.do! ile plakası
``34 TT 2000'' ve açıklaması ``benim arabam'' olan bir arabayı sisteme
eklettirdiğimiz için, bu kelimelerin araba listesinde olmasını beklememiz
normâldir. \PVerb!Run | Start! ile işletebileceğiniz ekleme kabul testinin
sonuçları \PVerb!Aggregate Report! biriminde şöyle
(\ref{testing:accept:assert:result}) gözükecektir. Eğer \PVerb!Error! kolonu
altında \PVerb!0.00%! görüyorsak, tüm testlerin başarı ile geçtiğini anlarız.
\begin{figure}[!hbp]
\center{
\scalebox{0.55}{
\includegraphics{./images/accept_test_7.eps}
}
}
\caption{\label{testing:accept:assert:result} Sonuçlar}
\end{figure}
Özet olarak, JMeter ile bir Web uygulamasını test etmek için, şunları yapmak
gerekir:
\begin{itemize}
\item Test edilen Action'a \PVerb!Http Request! ile bazı test değerleri
göndermek
\item \PVerb!struts-config.xml! dosyasına bakarak, iş bittikten sonra
yönlendirmenin nasıl yapılacağına bakmak
\item Bu yönlendirmeye göre, hangi değerlerin geleceğine göre bir kontrol
edici \PVerb!Response Assertion! birimini \PVerb!Http Request! altına eklemek
\end{itemize}
Eğer \PVerb!Http Request! birimlerini elle eklemek istemiyorsak,
\ref{perf:jmeter:record} bölümünde anlatıldığı gibi, kullanıcı programı tarayıcıda
kullanırken, hareketlerinin kaydedilmesini sağlayabiliriz.
\subsubsection{Kontrol Çeşitleri}
Bir \PVerb!Response Assertion! tanımlarken, yapacağımız ayarlardan bir tanesi,
``hangi içerik kontrolünün'' yapıldığını tanımlamaktır. Üstte gösterilen
kontrol, bir ``mevcudiyet'' kontrolü, ya da, bizim beklediğimiz değerin {\em
olmasını} bekleyen türden bir kontroldür. Fakat bir metnin olma kontrolünü
yaptığımız gibi, {\em olmamama} kontrolünü de yapabiliriz. Bu durum, meselâ bir
arabanın sistemden silindiği \PVerb!delete-car.do! Action'ı için gereklidir. Bu
Action bir araba sildiği için, mantıken o arabanın bir sonraki araba listesi
ekranında olmaması gerekecektir.
Eğer \PVerb!Response Assertion!'ın yaptığı kontrol çeşidini değiştirmek
istersek, bunu \PVerb!Pattern Matching Rules! ayarları altından
yapabiliriz. Meselâ, örnek olarak biraz önce eklediğimiz arabayı
\PVerb!delete-car.do! ile silelim, ve \PVerb!delete-car.do! altına koyacağımız
bir kontrol Şekil \ref{testing:accept:assert:not} üzerindeki gibi olsun. Bu
kontrolün detaylarında, uymama kontrolü için \PVerb!Contains! seçeneğine ek
olarak, \PVerb!Not! (değil) seçeneğini de seçmemiz gerekti. Eğer \PVerb!Not!
seçilmemiş olsaydı kontrol, pozitif bir kontrol olacaktı.
\begin{figure}[!hbp]
\center{
\scalebox{0.55}{
\includegraphics{./images/accept_test_5.eps}
}
}
\caption{\label{testing:accept:assert:not} Uymama Kontrolü}
\end{figure}
Bu testi de işletince, geriye gelen sonuç Şekil \ref{testing:accept:assert:both}
üzerindeki gibi olacaktır. Yine hata yüzdesi \PVerb!0.00%! seviyesindedir, yâni
tüm testler başarıyla geçmiştir.
\begin{figure}[!hbp]
\center{
\scalebox{0.55}{
\includegraphics{./images/accept_test_4.eps}
}
}
\caption{Ekleme ve Silme Testleri}
\end{figure}
\begin{figure}[!hbp]
\center{
\scalebox{0.55}{
\includegraphics{./images/accept_test_6.eps}
}
}
\caption{\label{testing:accept:assert:both} Ekleme ve Silme Test Sonuçları}
\end{figure}
\section{Ne Kadar Test Gerekli?}
Birim ve kabul testlerini ciddi bir şekilde uygulamaya karar verilince ve
teknikleri öğrenilince, programcıları en çok düşündüren kararlardan biri ``ne
kadar testin yeterli olduğu'' sorusudur. Ne de olsa testler, varlığı ve yokluğu
gereklilik kodları seviyesinde kontrol edilen işler değillerdir. Eğer yazmazsak,
yazılmayan testler yapılmayan kontrollerdir. Uygulamamız üzerinde bir değişiklik
olmaz.
\subsection{Birim Test Miktarı}
Birim testleri için ``ne kadar'' sorusunun cevabında iyi bir kulağa küpe kural
(rule of thumb) öncelikle kodun ne kadar çetrefil olduğu ile alâkalıdır. Kodun
çetrefilliğini en basit olarak, üstün körü bir şekilde, koda çıplak gözel bakış
kullanarak bile yapılabilir. Ne zaman ki bir metot kodlarının girinti
(indentation) seviyesi iki ya da daha fazla seviye içeri giriyor, o zaman o
metotu test etmemiz iyi olacaktır. Girinti seviyesi, bir metot içinde
\PVerb!while!, \PVerb!if!, \PVerb!else! komutlarının kullanımına bir işaret
olduğu için, basit bir litmus testi olarak faydalı bir göstergedir.
Diğer şartlarda, birim testi yazmak ve yazmamak kararı için her durumu ayrı ayrı
tartabiliriz. Biz, meselâ Hibernate kullanılan kurumsal kodlarımızda her kalıcı
nesne ve o nesne üzerindeki her ilişkiyi bir şekilde kullanan (genelde bir
\PVerb!get! komutu ile) bir testi yeterli buluyoruz. Bu çeşit bir test en
azından eşlemelerin doğru yapıldığına dair bize bir rahatlık vereceği için
yapılması faydalı olmaktadır. Ama her kalıcı nesne için dört işlemin (ekle, sil,
güncelle, yükle) test edilmesi fuzuli (overkill) olabilir. Bu tür birim
testlerini yazmak hızlı geliştirmenize engel olacağı için tarafımızdan tavsiye
edilmeyecektir.
\subsection{Kabul Test Miktarı}
Felsefe olarak şunu da eklemek gerekiyor. Kabul testleri birim testlerinden daha
önemlidir. Tecrübe gösteriyor ki, ufak, mikro seviyede yazılan testlerden geçen
ve doğru çalıştığı zannedilen bir uygulamanın bütününün çalışmaması
mümkündür. Özellikle birden fazla dış sistemin biraraya geldiği kurumsal
uygulama dünyasında iç ve dış entegrasyon, sistemin işleyişi için had safhada
önem taşır. O zaman kabul testlerini, özellikle anlattığımız şekilde otomatik
olanlarını hazırlamamız, ve güncel tutmamız önemlidir.
Kabul test miktarı açısından, en azından, her orta ve üstü zorlukta olan
gereklilik (functionality) ve bu gerekliliğin değişik senaryolarının test
edilmesi iyi olacaktır. Eğer daha fazla zaman ve kaynak ayırabiliyorsanız, en
basit sayfaları bile otomatik test havuzunuza dahil etmemiz, projemiz için ileri
safhalarda çok faydalı bir seçim olacaktır.