ASP.NET MVC’de denetleyiciyi test eden birimde gerçek bir değer var mı?


33

Umarım bu soru bazı ilginç cevaplar verir, çünkü bir süre beni rahatsız eden soru.

ASP.NET MVC’de denetleyiciyi test eden birimde gerçek bir değer var mı?

Bununla demek istediğim, çoğu zaman (ve ben deha değilim), denetleyici yöntemlerim, en karmaşıklarında bile böyledir:

public ActionResult Create(MyModel model)
{
    // start error list
    var errors = new List<string>();

    // check model state based on data annotations
    if(ModelState.IsValid)
    {
        // call a service method
        if(this._myService.CreateNew(model, Request.UserHostAddress, ref errors))
        {
            // all is well, data is saved, 
            // so tell the user they are brilliant
            return View("_Success");
        }
    }

    // add errors to model state
    errors.ForEach(e => ModelState.AddModelError("", e));

    // return view
    return View(model);
}

Ağır yüklerin çoğu MVC boru hattı veya servis kütüphanem tarafından yapılır.

Belki sormanız gereken sorular şunlar olabilir:

  • Bu yöntemi test eden birimin değeri ne olurdu?
  • NullReferenceException ile birlikte Request.UserHostAddressve kırılmaz ModelStatemıydı? Bunları alay etmeye çalışmalı mıyım?
  • eğer bu yöntemi tekrar kullanılabilir bir "yardımcı" ya da (kaç kere yaptığımı düşünürsem iyi olmalı) reddedersem, gerçekten test ettiğimde çoğunlukla "boru hattı" olduğu zaman bile faydalı olacağını denemek isterdim. Muhtemelen, Microsoft tarafından hayatının bir inç içinde test edilmiştir?

Sanırım benim açımdan gerçekten , aşağıdakileri yapmak tamamen anlamsız ve yanlış görünüyor

[TestMethod]
public void Test_Home_Index()
{
    var controller = new HomeController();
    var expected = "Index";
    var actual = ((ViewResult)controller.Index()).ViewName;
    Assert.AreEqual(expected, actual);
}

Açıkçası, bu abartılı ve anlamsız bir örnekle çılgına dönüyorum, ancak buraya eklemek için herhangi bir bilgeliği olan var mı?

Bekliyorum ... Teşekkürler.


Bence, belirli bir testteki ROI (Yatırımın Geri Dönüşü), sınırsız zamanınız ve paranız olmadığı sürece, çabaya değmez. Kevin'in kırılması daha muhtemel olan şeyleri kontrol etmek için işaret ettiği veya güvenle bir şeyi yeniden canlandırmakta ya da hata yayılmasının beklendiği gibi olmasını sağlamanıza yardımcı olacak testler yazarım. Gerekirse boru hattı testleri daha global / altyapı düzeyinde yapılabilir ve bireysel yöntemler düzeyinde çok az değeri olacaktır. Değeri değil, "küçük" demek. Bu durumda sizin için iyi bir yatırım getirisi sağlıyorsa, önce büyük balıkları yakalayın!
Şef,

Yanıtlar:


18

Çok basit bir şey için bile, bir birim testi birçok amaca hizmet eder

  1. Güven, yazıldığı beklenen çıktıya uyuyor. Doğru görüşü sağladığını doğrulamak önemsiz görünebilir, ancak sonuç ihtiyacın karşılandığına dair nesnel kanıtdır.
  2. Gerileme testi. Create yönteminin değişmesi gerekiyorsa, beklenen çıktı için hala bir birim testiniz vardır. Evet, çıktı değişebilir ve bu da kırılgan bir testle sonuçlanır ancak yine de yönetilmeyen değişiklik kontrolüne karşı bir kontrol

Bu özel eylem için aşağıdakileri test ederim

  1. _MyService null olursa ne olur?
  2. _MyService.Create bir İstisna atarsa, ele alınması gerekenleri atarsa ​​ne olur?
  3. Başarılı bir _myService.Create, _Success görünümünü döndürür mü?
  4. Hatalar ModelState'e yayılıyor mu?

NullReferenceException için İsteği ve Modeli kontrol etmeyi işaret ettiniz ve bence ModelState.IsValid, NullReference for Model ile ilgilenecek.

Talebi alay etmek, üretimde genellikle imkansız olan, ama bir Birim Testinde gerçekleşebilecek Boş Bir İsteğe karşı korunmanıza izin verir. Bir Entegrasyon Testinde, farklı UserHostAddress değerleri sağlamanıza izin verir (Bir istek, kontrol söz konusu olduğunda kullanıcı girişidir ve buna göre test edilmelidir)


Merhaba Kevin, cevaplamak için zaman ayırdığınız için teşekkür ederiz. Bir süre sonra başkalarının bir şeyle gelip gelmediğini görmek için ayrılacağım.
LiverpoolsNumber9

Spifty. Sana yardım etmesine sevindim.
Kevin

3

Kontrolörlerim de çok küçük. Denetleyicilerdeki "mantık" ın çoğu, filtre öznitelikleri (yerleşik ve elle yazılmış) kullanılarak gerçekleştirilir. Bu nedenle, denetleyicimin genellikle yalnızca bir avuç işi vardır:

  • HTTP sorgu dizelerinden, form değerlerinden vb. Modeller oluşturun.
  • Bazı temel doğrulama işlemleri yapın
  • Verilerime veya iş katmanıma çağrı yap
  • Bir oluşturmak ActionResult

Model bağlamanın çoğu ASP.NET MVC tarafından otomatik olarak yapılır. DataAnnotations, benim için de onaylama işleminin çoğunu idare ediyor.

Test etmek için çok az olsa bile, ben hala genellikle onları yazmak. Temel olarak, depolarımın çağrıldığını ve doğru ActionResulttipin geri döndürüldüğünü test ediyorum . ViewResultDoğru görünüm yolunun döndürülmesini ve görünüm modelinin beklediğim gibi görünmesini sağlamak için kullanışlı bir yöntemim var . Doğru kontrolör / eylem için ayarlanmış kontrol etmek için başka var RedirectToActionResult. JsonResultVb. İçin başka testlerim var .

ControllerSınıfı alt sınıflamanın talihsiz bir sonucu , HttpContextdahili olarak kullanan birçok kolaylık yöntemi sağlamasıdır . Bu, ünitenin test cihazını test etmesini zorlaştırır. Bu nedenle, genellikle HttpContextbir arabirimin arkasına bağımlı çağrılar koyar ve bu arayüzü denetleyicinin yapıcısına iletirim (denetleyicileri benim için oluşturmak için Ninject web uzantısını kullanıyorum). Bu arabirim genellikle oturuma, yapılandırma ayarlarına, IP ilke ve URL yardımcılarına erişmek için yardımcı özellikleri yapıyorum.

Bu çok fazla titizlik gerektirir, ancak buna değer olduğunu düşünüyorum.


Cevaplamak için zaman ayırdığınız için teşekkür ederiz, ancak 2 sorunu hemen. İlk olarak, birim testlerinde "yardımcı yöntemler" v. tehlikeli. İkincisi, "depolarımın çağrıldığını test et" - bağımlılık enjeksiyonunu mu kastediyorsun?
LiverpoolsNumber9

Uygunluk yöntemleri neden tehlikeli olabilir? BaseControllerTestsHepsinin yaşadığı bir sınıfım var . Depolarım ile alay ediyorum. Onları Ninject kullanarak bağlarım.
Travis Parks

Yardımcınız / hatalarınızda bir hata veya yanlış bir varsayım yaptıysanız ne olur? Diğer bir nokta, yalnızca bir entegrasyon testinin (yani uçtan uca) depolarınızın aranıp aranmadığını "test edebilmesi" idi. Birim testinde yine de depolarınızı "yeniler" veya manuel olarak alay edersiniz.
LiverpoolsNumber9

Depoyu yapıcıya iletirsiniz. Test sırasında alay ettin. Sahnenin beklendiği gibi davrandığından emin olursunuz. Yardımcılar ActionResult, geçen URL'leri, modelleri vb. İncelemek için basitçe yapıları kaldırır
Travis Parks

Yeterince adil - "Depolarımın çağrıldığını test et" derken ne demek istediğinizi biraz yanlış anladım.
LiverpoolsNumber9

2

Açıkçası, bazı kontrolörler bundan çok daha karmaşık ancak tamamen sizin örneğinize dayanıyor:

Servisim bir istisna atarsa ​​ne olur?

Yan not olarak.

Ayrıca, bir listeyi referansla geçirme bilgisini sorgularım (c # yine de referansla geçtiğinden bu gerekli değildir) - hizmetin hata mesajlarını pompalamak için kullanabileceği bir errorAction eylemi (Eylem) iletmek hangisi daha sonra istediğiniz şekilde ele alınabilir (belki listeye eklemek istersiniz, belki bir model hatası eklemek istersiniz, belki de günlüğe kaydetmek istersiniz).

Örnekte:

ref hataları yerine, örneğin (s) => ModelState.AddModelError ("", s) yapın.


Söylemeye değer, bu hizmetinizin aynı uygulamada bulunduğunu varsayar, aksi halde seri hale getirme sorunları devreye girer.
Michael,

Servis ayrı bir dll olacaktır. Ama yine de, muhtemelen haklısın "ref". Diğer noktada, benim Servisimin bir istisna atması önemli değil. Servisimi test etmiyorum - Buradaki yöntemleri ayrı ayrı test ederim. Sadece ActionResult "ünitesini" (muhtemelen) alaycı bir myService ile test etmekten bahsediyorum.
LiverpoolsNumber9

Hizmetinizle kontrol cihazınız arasında 1: 1 eşlemeniz var mı? Değilse, bazı kontrolörler birden fazla servis çağrısı kullanıyor mu? Eğer öyleyse, bu etkileşimleri test edebilirsiniz?
Michael,

Hayır. Günün sonunda, servis yöntemleri girdi alır (genellikle bir görünüm modeli veya hatta sadece dizeler / inçler), "şeyler yaparlar", sonra yanlışsa bir bool / hata döndürürler. Denetleyicilerle servis katmanı arasında "doğrudan" bir bağlantı yoktur. Tamamen ayrılmıştır.
LiverpoolsNumber9 16

Evet, anlıyorum ki, kontrolörler ve servis katmanı arasındaki ilişkisel modeli anlamaya çalışıyorum - her kontrol biriminin ilgili servis yöntemine sahip olmadığını farzederek, bazı kontrol cihazlarının kullanılması gerekebileceğinin nedeni olur. birden fazla servis yöntemi?
Michael,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.