Test verisi oluşturmak çoğu yazılımcı için kazananı olmayan bir savaş.
Ne kadar test case oluşturursanız oluşturun yeterli gelmiyor. Hele bir
de iş uygulaması yazıyorsanız işler içinden çıkılmaz bir hale geliyor.
Düşünelim ki bir araç kiralama firması için uygun araçları listeleyen
bir modül yazıyorsunuz ve firmanın iş kuralları yeterince karışık. Örnek
vermek gerekirse 2 yaşından büyük araçlara 3 günden fazla süren
kiralamalar için müsaitlik verme gibi bir kıstasınız var. Bu
sınırlamalarınızın sayısı çok fazla ise her durum için ayrı ayrı testini
yazmak durumundasınız. Her durum için göre poco, pojo, dto ları uygun
değişkenler ile doldurmak anlamına geliyor. Hali hazırda çalıştığım
uygulamadan bir screenshot paylaşayım.
Görevi sadece test verisi ve işbirlikçilerini(collaborator) oluşturmak
olan init fonksiyonu sadece 307 satırcık. Kabaca 300 satır diyebiliriz.
Bu noktada yardımınıza fuzzing koşuyor.
Fuzzing yanlış, bozulmuş, eksik veya özel karakterler içeren streamler
ile sistemi penetre etmeye yarayan bir teknik. Amacı ise uygulamayı hata
vermeye zorlamak. Genel olarak güvenlik amacı ile kullanılsa testcase
oluşturmak için de kullanılıyor.Hatanın yakalandığı yerde buffer
overflow gibi bir hata alınması durumunda code/ command injection a
kadar giden bir süreç. Yukarıda bahsettiğim araç kiralama uygulaması
uygunluk modulünü aşağıdaki gibi implement edelim.
publicclassVehicle{publicDateTimeYear{get;set;}publicintAge{get;set;}publicSegmentSegment{get;set;}publicTransmissonTransmisson{get;set;}publicPowerTypePowerType{get;set;}}publicclassAvailabilityResponse{publicList<Vehicle>Vehicles{get;set;}}publicclassAvailabilityQuery{publicSegmentSegment{get;set;}publicTransmissonTransmisson{get;set;}publicPowerTypePowerType{get;set;}publicDateTimeBegin{get;set;}publicDateTimeEnd{get;set;}publicoverridestringToString(){return$"Begin = {Begin}, End = {End}, PowerType = {PowerType}, Transmisson = {Transmisson}, Segment = {Segment} ";}}publicinterfaceIVehicleAvailabilityService{AvailabilityResponseCheck(AvailabilityQueryquery);}publicclassVehicleAvailabilitySearchService:IVehicleAvailabilityService{publicAvailabilityResponseCheck(AvailabilityQueryquery){// ...if(query.Begin.Date==DateTime.Today){thrownewInvalidOperationException("Special corner case or null pointer exception");}elseif(query.Segment==Segment.NONE){thrownewArgumentException("Segment is mandatory in case of very complex scenario happened");}// ...returnnewAvailabilityResponse();}}
Gerçek işi yapan kısmı çıkarınca yukarıdaki gibi bir kod parçası elde
ediyoruz. Ortaya çıkabilecek runtime exceptionları simule etmek için
tuhaf exceptionlar atıyor çok takılmayın. Gelelim fuzzer
implementasyonumuza. Yapılan işlem ilgili veri tipleri için tamamen
random veri atamak. Başlıkta dikkat ettiğiniz gibi tamamen aptal (Dumb)
bir kutu.
Peki testimiz nasıl işliyor. Test altındaki servisimizin (SUT) exception
atmasını beklemiyoruz. İnput olarak verilen bir query ye cevap olarak
exception atması modulümüzde bir şeylerin doğru gitmediğini gösteriyor.
publicclassVehicle{publicDateTimeYear{get;set;}publicintAge{get;set;}publicSegmentSegment{get;set;}publicTransmissonTransmisson{get;set;}publicPowerTypePowerType{get;set;}}publicclassAvailabilityResponse{publicList<Vehicle>Vehicles{get;set;}}publicclassAvailabilityQuery{publicSegmentSegment{get;set;}publicTransmissonTransmisson{get;set;}publicPowerTypePowerType{get;set;}publicDateTimeBegin{get;set;}publicDateTimeEnd{get;set;}publicoverridestringToString(){return$"Begin = {Begin}, End = {End}, PowerType = {PowerType}, Transmisson = {Transmisson}, Segment = {Segment} ";}}publicinterfaceIVehicleAvailabilityService{AvailabilityResponseCheck(AvailabilityQueryquery);}publicclassVehicleAvailabilitySearchService:IVehicleAvailabilityService{publicAvailabilityResponseCheck(AvailabilityQueryquery){// ...if(query.Begin.Date==DateTime.Today){thrownewInvalidOperationException("Special corner case or null pointer exception");}elseif(query.Segment==Segment.NONE){thrownewArgumentException("Segment is mandatory in case of very complex scenario happened");}// ...returnnewAvailabilityResponse();}}
Testi 1000 defa çalıştırdığım durumda aldığım hatalar aşağıdaki gibi.
Begin = 28.08.2017 14:14:03, End = 13.01.2018 14:14:03,
PowerType = NONE, Transmisson =NONE, Segment = NONE> Segment is
mandatory in case of very complex scenario happened
Begin = 18.08.2017 14:14:03, End = 20.03.2018 14:14:03, PowerType =
GASOLINE, Transmisson = AUTOMATIC, Segment = B > Special corner case
or null pointer exception
Bunun içine genetik ve arama algoritmaları girmesi ile smart fuzzing
adını alıyor. Sisteme random veri vermek yerine potansiyel hata
durumlarına daha yakın veri üretip tüm processi sizin için daha
kolay hale getiriyor.
Artıları
Test etmeyi düşünmediğiniz durumları bulabilir.
Bir kere çalışır duruma getirdiğinizde sizden bağımsız olarak
çok fazla sayıda test çalıştırabilir.
Eksileri
Kimi buglar gözden kaçabilir.
Çok fazla bandwidth tüketir. Dolayısı ile ayrı dedike makinalarla,
farklı subnetler üzerinde test etmek gerekebilir.