HTTP İstek Testi: Bir Geliştiricinin Hayatta Kalma Aracı
Yayınlanan: 2022-03-11Bir Test Paketi Uygun Değilse Ne Yapmalı?
Programcılar ve/veya müşterilerimizin - hem beklenen çıktıyı hem de bu çıktı için otomatik testleri yazmak için sınırlı kaynaklara sahip olduğumuz zamanlar vardır. Uygulama yeterince küçük olduğunda, köşeleri kesebilir ve testleri atlayabilirsiniz, çünkü (çoğunlukla) bir özellik eklediğinizde, bir hatayı düzelttiğinizde veya yeniden düzenlediğinizde kodun başka yerlerinde neler olduğunu hatırlarsınız. Bununla birlikte, her zaman küçük uygulamalarla çalışmayacağız, ayrıca zamanla daha büyük ve daha karmaşık hale gelme eğilimindedirler. Bu, manuel testi zorlaştırır ve çok sinir bozucu hale getirir.
Son birkaç projemde, otomatik test olmadan çalışmak zorunda kaldım ve dürüst olmak gerekirse, müşterinin bir kod zorlamasından sonra bana e-posta göndermesi ve uygulamanın koda dokunmadığım yerlerde uygulamanın bozulduğunu söylemesi utanç vericiydi.
Bu nedenle, müşterimin herhangi bir bütçesi olmadığı veya herhangi bir otomatik test çerçevesi ekleme niyeti olmadığı durumlarda, her bir sayfaya bir HTTP isteği göndererek, yanıt başlıklarını ayrıştırarak ve '200'ü arayarak tüm web sitesinin temel işlevlerini test etmeye başladım. tepki. Kulağa sade ve basit geliyor, ancak herhangi bir test, birim, işlevsellik veya entegrasyon yazmak zorunda kalmadan aslına uygunluğu sağlamak için yapabileceğiniz çok şey var.
Otomatik Test
Web geliştirmede otomatik testler üç ana test türünden oluşur: birim testleri, işlevsel testler ve entegrasyon testleri. Her şeyin bir bütün uygulama olarak sorunsuz çalıştığından emin olmak için genellikle birim testlerini işlevsel ve entegrasyon testleriyle birleştiririz. Bu testler birlikte veya sıralı olarak (tercihen tek komutla veya tıklamayla) çalıştırıldığında, bunları birim veya değil, otomatik testler olarak adlandırmaya başlarız.
Büyük ölçüde bu testlerin amacı (en azından web geliştirmede), tüm uygulama sayfalarının sorunsuz, ölümcül (uygulamanın durdurulması) hata veya buglardan arındırılmış olmasını sağlamaktır.
Birim Testi
Birim testi, kodun en küçük parçalarının - birimlerin - doğru çalışma için bağımsız olarak test edildiği bir yazılım geliştirme sürecidir. İşte Ruby'de bir örnek:
test “should return active users” do active_user = create(:user, active: true) non_active_user = create(:user, active: false) result = User.active assert_equal result, [active_user] end
Fonksiyonel test
İşlevsel test, sistem veya yazılımın özelliklerini ve işlevselliğini kontrol etmek için kullanılan, arıza yolları ve sınır durumları da dahil olmak üzere tüm kullanıcı etkileşimi senaryolarını kapsayacak şekilde tasarlanmış bir tekniktir.
Not: Tüm örneklerimiz Ruby'dedir.
test "should get index" do get :index assert_response :success assert_not_nil assigns(:object) end
Entegrasyon Testi
Modüller birim test edildikten sonra, kombinasyon davranışını kontrol etmek ve gereksinimlerin doğru bir şekilde uygulandığını doğrulamak için sırayla tek tek entegre edilirler.
test "login and browse site" do # login via https https! get "/login" assert_response :success post_via_redirect "/login", username: users(:david).username, password: users(:david).password assert_equal '/welcome', path assert_equal 'Welcome david!', flash[:notice] https!(false) get "/articles/all" assert_response :success assert assigns(:articles) end
İdeal Bir Dünyada Testler
Test, endüstride yaygın olarak kabul görmektedir ve mantıklıdır; iyi testler şunları yapmanızı sağlar:
- Kalite, tüm uygulamanızı en az insan çabasıyla garanti eder
- Kodunuzun test hatalarından tam olarak nerede koptuğunu bildiğiniz için hataları daha kolay belirleyin
- Kodunuz için otomatik belgeler oluşturun
- Stack Overflow'taki bazı adamlara göre, "sırada ne yazacağınızı bilmiyorsanız veya önünüzde göz korkutucu bir görev varsa, küçük yazarak başlayın" demenin mizahi bir yolu olan 'kodlama kabızlığı'ndan kaçının. ”
Testlerin ne kadar harika olduğu ve dünyayı nasıl değiştirdikleri ve yada yada yada nasıl oldukları hakkında devam edebilirim, ama siz anladınız. Kavramsal olarak, testler harika.
Gerçek Dünyada Testler
Her üç test türünün de faydaları olsa da, çoğu projede yazılmazlar. Niye ya? Pekala, onu parçalayayım:
Zaman/Son Tarihler
Herkesin son teslim tarihi vardır ve yeni testler yazmak, birini karşılamanın önüne geçebilir. Bir uygulama ve ilgili testleri yazmak bir buçuk (veya daha fazla) zaman alabilir. Şimdi, bazılarınız sonuçta kazanılan zamandan bahsederek buna katılmıyorsunuz, ancak durumun böyle olduğunu düşünmüyorum ve nedenini 'Difference of Opinion'da açıklayacağım.
Müşteri Sorunları
Çoğu zaman müşteri, testin ne olduğunu veya uygulama için neden değeri olduğunu gerçekten anlamaz. Müşteriler, hızlı ürün teslimatı ile daha fazla ilgilenme eğilimindedir ve bu nedenle programatik testleri verimsiz olarak görürler.
Veya müşterinin bu testleri uygulamak için gereken ekstra süre için ödeme yapacak bütçesi olmaması kadar basit olabilir.
Bilgi eksikliği
Gerçek dünyada testin varlığından haberdar olmayan oldukça büyük bir geliştirici kabilesi var. Her konferansta, buluşmada, konserde (rüyalarımda bile), nasıl test yazılacağını bilmeyen, neyi test edeceğini bilmeyen, test çerçevesini nasıl kuracağını bilmeyen geliştiricilerle tanışıyorum. üzerinde. Testler okullarda tam olarak öğretilmez ve bunları çalıştırmak için çerçeveyi kurmak/öğrenmek zor olabilir. Yani evet, giriş için kesin bir engel var.
'Çok İş Var'
Yazma testleri, hem yeni hem de deneyimli programcılar için, hatta dünyayı değiştiren dahi türleri için bile bunaltıcı olabilir ve üstelik, yazma testleri heyecan verici değildir. Kişi, “Müvekkilimi etkileyecek sonuçlara sahip önemli bir özelliği uygulayabilecekken neden heyecan verici olmayan yoğun işlerle meşgul olayım?” diye düşünebilir. Bu zor bir argüman.
Son olarak, testler yazmak zordur ve bilgisayar bilimleri öğrencileri bunun için eğitilmemiştir.
Oh, ve birim testleriyle yeniden düzenleme yapmak eğlenceli değil.
Fikir Farkı
Benim düşünceme göre, birim testi algoritmik mantık için anlamlıdır, ancak yaşayan kodu koordine etmek için çok fazla değildir.
İnsanlar, testler yazmak için önceden fazladan zaman ayırıyor olsanız bile, hata ayıklarken veya kod değiştirirken saatler sonra tasarruf etmenizi sağladığını iddia ediyor. Farkına varmak ve bir soru sormak için yalvarıyorum: Kodunuz statik mi yoksa hiç değişiyor mu?
Çoğumuz için, sürekli değişiyor. Başarılı bir yazılım yazıyorsanız, her zaman özellikler ekliyor, mevcut olanları değiştiriyor, kaldırıyor, yiyorsunuz, ne olursa olsun ve bu değişikliklere uyum sağlamak için testlerinizi değiştirmeye devam etmelisiniz ve testlerinizi değiştirmek zaman alır.
Ancak, Bir Tür Teste İhtiyacınız Var
Hiç kimse, herhangi bir testten yoksun olmanın olası en kötü durum olduğunu iddia etmeyecektir. Kodunuzda değişiklik yaptıktan sonra, gerçekten çalıştığını onaylamanız gerekir. Pek çok programcı, temel bilgileri manuel olarak test etmeye çalışır: Sayfa, tarayıcıda görüntüleniyor mu? Form gönderiliyor mu? Doğru içerik görüntüleniyor mu? Vesaire, ama bence bu barbarca, verimsiz ve emek yoğun.
Yerine Ne Kullanırım
Bir web uygulamasını manuel veya otomatik olarak test etmenin amacı, herhangi bir sayfanın kullanıcının tarayıcısında herhangi bir önemli hata olmadan oluşturulduğunu ve içeriğini doğru şekilde gösterdiğini doğrulamaktır. Bunu başarmanın bir yolu (ve çoğu durumda daha kolay bir yol), uygulamanın uç noktalarına HTTP istekleri göndermek ve yanıtı ayrıştırmaktır. Yanıt kodu, sayfanın başarıyla teslim edilip edilmediğini size söyler. HTTP isteğinin yanıt gövdesini ayrıştırarak ve belirli metin dizisi eşleşmelerini arayarak içeriği test etmek kolaydır veya bir adım daha meraklısı olabilir ve nokogiri gibi web kazıma kitaplıklarını kullanabilirsiniz.
Bazı uç noktalar için kullanıcı oturum açması gerekiyorsa, oturum açmak için mekanikleştirme veya belirli bağlantılara tıklama gibi etkileşimleri otomatikleştirmek için (entegrasyon testleri yaparken idealdir) tasarlanmış kitaplıkları kullanabilirsiniz. Gerçekten, otomatik testin büyük resminde, bu, entegrasyon veya işlevsel teste çok benziyor (bunları nasıl kullandığınıza bağlı olarak), ancak yazması çok daha hızlı ve mevcut bir projeye dahil edilebilir veya yeni bir projeye eklenebilir. , tüm test çerçevesini kurmaktan daha az çabayla. Dikkat!
Uç durumlar, çok çeşitli değerlere sahip büyük veritabanlarıyla uğraşırken başka bir sorun sunar; Uygulamamızın beklenen tüm veri kümelerinde sorunsuz çalışıp çalışmadığını test etmek göz korkutucu olabilir.

Bunu yapmanın bir yolu, tüm uç durumları tahmin etmek (ki bu sadece zor değil, çoğu zaman imkansızdır) ve her biri için bir test yazmaktır. Bu, kolayca yüzlerce kod satırına dönüşebilir (korkuyu hayal edin) ve bakımı zahmetli olabilir. Yine de, HTTP istekleri ve yalnızca bir kod satırıyla, bu tür uç durumları doğrudan üretimden alınan, yerel olarak geliştirme makinenize veya bir hazırlama sunucusuna indirilen veriler üzerinde test edebilirsiniz.
Şimdi, elbette, bu test tekniği gümüş bir kurşun değildir ve diğer yöntemlerde olduğu gibi birçok eksikliği vardır, ancak bu tür testleri yazması ve değiştirmesi daha hızlı ve daha kolay buluyorum.
Uygulamada: HTTP istekleriyle test etme
Herhangi bir eşlik eden test olmadan kod yazmanın iyi bir fikir olmadığını zaten belirlediğimizden, tüm bir uygulama için en temel testim, tüm sayfalarına yerel olarak HTTP istekleri göndermek ve yanıt başlıklarını bir için ayrıştırmaktır. 200
(veya istenen) kod.
Örneğin, yukarıdaki testleri (belirli bir içerik ve önemli bir hata arayanlar) yerine bir HTTP isteği ile (Ruby'de) yazacak olsaydık, şöyle bir şey olurdu:
# testing for fatal error http_code = `curl -X #{route[:method]} -s -o /dev/null -w "%{http_code}" #{Rails.application.routes.url_helpers.articles_url(host: 'localhost', port: 3000) }` if http_code !~ /200/ return “articles_url returned with #{http_code} http code.” end # testing for content active_user = create(:user, name: “user1”, active: true) non_active_user = create(:user, name: “user2”, active: false) content = `curl #{Rails.application.routes.url_helpers.active_user_url(host: 'localhost', port: 3000) }` if content !~ /#{active_user.name}/ return “Content mismatch active user #{active_user.name} not found in text body” #You can customise message to your liking end if content =~ /#{non_active_user.name}/ return “Content mismatch non active user #{active_user.name} found in text body” #You can customise message to your liking end
curl -X #{route[:method]} -s -o /dev/null -w "%{http_code}" #{Rails.application.routes.url_helpers.articles_url(host: 'localhost', port: 3000) }
bir çok test senaryosunu kapsar; makalenin sayfasında bir hata oluşturan herhangi bir yöntem burada yakalanır, bu nedenle tek bir testte yüzlerce kod satırını etkili bir şekilde kapsar.
Özellikle içerik hatasını yakalayan ikinci kısım, bir sayfadaki içeriği kontrol etmek için birden çok kez kullanılabilir. (Daha karmaşık istekler mechanize
kullanılarak işlenebilir, ancak bu, bu blogun kapsamı dışındadır.)
Şimdi, belirli bir sayfanın geniş, çeşitli bir veritabanı değerleri kümesinde çalışıp çalışmadığını test etmek istediğiniz durumlarda (örneğin, makale sayfası şablonunuz üretim veritabanındaki tüm makaleler için çalışıyor), şunları yapabilirsiniz:
ids = Article.all.select { |post| `curl -s -o /dev/null -w “%{http_code}” #{Rails.application.routes.url_helpers.article_url(post, host: 'localhost', port: 3000) }`.to_i != 200).map(&:id) return ids
Bu, veritabanındaki oluşturulmamış tüm makalelerin bir dizi kimliğini döndürür, böylece artık belirli makale sayfasına manuel olarak gidebilir ve sorunu kontrol edebilirsiniz.
Şimdi, bu test yönteminin bağımsız bir komut dosyasını test etme veya bir e-posta gönderme gibi belirli durumlarda çalışmayabileceğini ve her test için bir uç noktaya doğrudan çağrılar yaptığımız için birim testlerinden inkar edilemez şekilde daha yavaş olduğunu anlıyorum. birim testlerine veya işlevsel testlere veya her ikisine birden sahip olamazsınız, bu hiç yoktan iyidir.
Bu testleri yapılandırma konusunda nasıl bir yol izlersiniz? Küçük, karmaşık olmayan projelerde, tüm testlerinizi tek bir dosyaya yazabilir ve değişikliklerinizi gerçekleştirmeden önce her seferinde bu dosyayı çalıştırabilirsiniz, ancak çoğu proje bir dizi test gerektirecektir.
Test ettiğim şeye bağlı olarak, genellikle uç nokta başına iki ila üç test yazarım. Ayrıca bireysel içeriği test etmeyi deneyebilirsiniz (birim testine benzer), ancak her birim için bir HTTP çağrısı yapacağınız için bunun gereksiz ve yavaş olacağını düşünüyorum. Ancak öte yandan, daha temiz ve anlaşılması kolay olacaklardır.
Testlerinizi, her ana uç noktanın kendi dosyasına sahip olacak şekilde normal test klasörünüze koymanızı tavsiye ederim (örneğin, Rails'de her model/kontrolörün her birinde bir dosya olacaktır) ve bu dosya, yaptığımız şeye göre üç bölüme ayrılabilir. test ediyorlar. Sıklıkla en az üç testim olur:
Test Bir
Sayfanın herhangi bir önemli hata olmadan döndüğünü kontrol edin.
Post
için tüm uç noktaların bir listesini nasıl yaptığımı ve her sayfanın hatasız bir şekilde oluşturulup oluşturulmadığını kontrol etmek için üzerinde yinelendiğime dikkat edin. Her şeyin yolunda gittiğini ve tüm sayfaların oluşturulduğunu varsayarsak, terminalde şöyle bir şey göreceksiniz: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of failed url(s) -- []
Herhangi bir sayfa görüntülenmezse, bunun gibi bir şey göreceksiniz (bu örnekte, posts/index page
hata var ve bu nedenle oluşturulamıyor): ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of failed url(s) -- [{:url=>”posts_url”, :params=>[], :method=>”GET”, :http_code=>”500”}]
Test İki
Beklenen tüm içeriğin orada olduğunu onaylayın:
Beklediğimiz tüm içerik sayfada bulunursa, sonuç şöyle görünür (bu örnekte posts/:id
id'nin bir gönderi başlığı, açıklaması ve durumu olduğundan emin oluruz): ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of content(s) not found on Post#show page with post id: 1 -- []
Sayfada beklenen herhangi bir içerik bulunamazsa (burada sayfanın gönderi durumunu göstermesini bekliyoruz - gönderi etkinse 'Etkin', gönderi devre dışıysa 'Devre dışı') sonuç şöyle görünür: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of content(s) not found on Post#show page with post id: 1 -- [“Active”]
Üçüncü Test
Sayfanın tüm veri kümelerinde (varsa) oluşturulduğunu kontrol edin:
Tüm sayfalar hatasız olarak oluşturulursa, boş bir liste alırız: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of post(s) with error in rendering -- []
Bazı kayıtların içeriğinin oluşturulmasında sorun varsa (bu örnekte, ID 2 ve 5 olan sayfalar hata veriyor) sonuç şöyle görünür: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of post(s) with error on rendering -- [2,5]
Yukarıdaki gösteri koduyla uğraşmak istiyorsanız, işte benim github projem.
Peki Hangisi Daha İyi? Değişir…
Aşağıdaki durumlarda HTTP İsteği testi en iyi seçeneğiniz olabilir:
- Bir web uygulamasıyla çalışıyorsunuz
- Zaman sıkıntısı içindesiniz ve hızlı bir şeyler yazmak istiyorsunuz
- Testlerin yazılmadığı, önceden var olan büyük bir projeyle çalışıyorsunuz, ancak yine de kodu kontrol etmenin bir yolunu istiyorsunuz.
- Kodunuz basit istek ve yanıt içeriyor
- Zamanınızın büyük bir kısmını testleri sürdürmekle harcamak istemezsiniz (bir yerde birim testi = bakım cehennemi okudum ve ona kısmen katılıyorum)
- Bir uygulamanın mevcut bir veritabanındaki tüm değerlerde çalışıp çalışmadığını test etmek istiyorsunuz.
Geleneksel testler şu durumlarda idealdir:
- Komut dosyaları gibi bir web uygulamasından başka bir şeyle uğraşıyorsunuz
- Karmaşık, algoritmik kod yazıyorsunuz
- Test yazmaya adamak için zamanınız ve bütçeniz var
- İşletme, hatasız veya düşük hata oranı gerektirir (finans, geniş kullanıcı tabanı)
Makaleyi okuduğunuz için teşekkürler; Artık, varsayılan olarak uygulayabileceğiniz, zamanı geldiğinde güvenebileceğiniz bir test yöntemine sahip olmalısınız.
- Performans ve Verimlilik: HTTP/3 ile Çalışmak
- Şifreli Tutun, Güvende Tutun: ESNI, DoH ve DoT ile Çalışmak