it-swarm-tr.com

Sahte, alaycı ve inatçılık arasındaki fark nedir?

Bu terimleri nasıl kullandığımı biliyorum, ancak birim testleri için faking, mocking ve stubbing için kabul edilmiş tanımlar olup olmadığını merak ediyorum. Bunları testleriniz için nasıl tanımlarsınız? Her birini kullanabileceğiniz durumları açıklayın.

İşte onları nasıl kullanıyorum:

Fake: bir arayüz uygulayan ancak sabit veri içeren ve mantık içermeyen bir sınıf. Yalnızca uygulamaya bağlı olarak "iyi" veya "kötü" verileri döndürür.

Mock: bir arabirim uygulayan ve belirli yöntemlerden atmak için değerleri döndürmek/istisnalar için dinamik olarak ayarlama yeteneğine sahip ve belirli yöntemlerin çağrılıp çağrılmadığını kontrol etme olanağı sağlayan bir sınıf.

Stub: Sahte bir sınıf gibi, ancak bu yöntemlerin çağrıldığını/çağrılmadığını doğrulama yeteneği sağlamaz.

Alaylar ve taslaklar, alaycı bir çerçeve tarafından elle üretilebilir veya üretilebilir. Sahte sınıflar elle üretilir. Sınıfla bağımlı sınıflarım arasındaki etkileşimi doğrulamak için alaycı kullanıyorum. Etkileşimleri doğruladıktan ve kodum üzerinden alternatif yolları test ettiğimden sonra taslakları kullanıyorum. Sahte sınıfları öncelikle veri bağımlılıklarını soyutlamak için ya da alaylar/saplamalar her seferinde ayarlamak için çok can sıkıcı olduğunda kullanırım.

563
tvanfosson

Bazı bilgiler alabilirsiniz:

Martin Fowler, Mock ve Stub hakkında 'dan

Fake nesnelerinin gerçekte çalışma uygulamaları vardır, ancak genellikle üretime uygun olmayan bazı kısayollar kullanırlar

Stubs, test sırasında yapılan aramalara, genellikle test için programlananların dışındaki hiçbir şeye cevap vermeyen hazır cevaplar sağlar. Saplamalar ayrıca, 'gönderdiği' mesajları hatırlayan bir e-posta ağ geçidi saplaması ya da belki sadece 'kaç tane mesaj gönderdiğini' hatırlatan aramalar hakkındaki bilgileri de kaydedebilir.

Mocks, burada bahsettiğimiz şey: almaları beklenen çağrıların bir belirtimini oluşturan beklentileri önceden programlanmış nesneler.

Xunitpattern 'den:

Fake: SUT'un dayandığı ve SUT'u gerçek yerine kullanması için talimat veren bir bileşen tarafından sağlanan aynı fonksiyonelliğin çok hafif bir uygulamasını edinir veya inşa ederiz.

Stub: Bu uygulama, SUT'daki aramalara SUT içindeki Test Edilmemiş Kod (X Üretiminde Hatalar bölümüne bakınız) uygulayacak değerlerle (veya istisnalar) cevap verecek şekilde yapılandırılmıştır. Bir Test Stub'ı kullanmanın kilit bir göstergesi, SUT'nin dolaylı girişlerini kontrol edememenin sebep olduğu Test Edilmemiş Kod'a sahip olmasıdır.

SUT (Test Edilen Sistem) 'in bağlı olduğu nesne ile aynı arayüzü uygulayan Mock Object. SUT üzerinde yöntemlerin yan etkilerini gözlemlememekten kaynaklanan bir Test Edilmemiş Gereksinim (Sayfa X'teki Üretim Hatalarına bakınız) gerekmemesi için Davranış Doğrulaması yapmamız gerektiğinde gözlem nesnesi olarak bir Sahte Nesne kullanabiliriz.

Şahsen

Basitleştirmeye çalışıyorum: Mock ve Stub. Test sınıfına ayarlanan bir değer döndüren bir nesne olduğunda Mock kullanıyorum. Stub'ı test edilecek bir Interface veya Abstract sınıfını taklit etmek için kullanıyorum. Aslında, sizin ne dediğiniz önemli değil, hepsi üretimde kullanılmayan sınıflar ve test için yardımcı sınıflar olarak kullanılıyor.

452

Stub - yöntem çağrılarına önceden tanımlanmış cevaplar sağlayan bir nesne. 

Mock - beklentilerini belirlediğin bir nesne.

Sahte - Sınırlı yeteneklere sahip bir nesne (test amacıyla), örn. sahte bir web servisi. 

Test Double, taslaklar, alaylar ve taklitler için kullanılan genel terimdir. Ancak, gayrı resmi olarak, insanların basitçe "sahte" dediklerini duyarsınız. 

171
Mike

Bu sorunun çok uzun zamandır ortaya çıktığını ve Roy Osherove'in "Birim Test Etme Sanatı" nı temel alan bir kimsenin henüz bir cevap vermediğine şaşırdım.

"3.1 taslakların tanıtılması" bölümünde bir taslak olarak tanımlanmaktadır:

Bir saplama, mevcut bir bağımlılık için kontrol edilebilir bir ikamedir. (veya ortak çalışan) sistemde. Bir saplama kullanarak, kodunuzu .__ olmadan test edebilirsiniz. doğrudan bağımlılıkla başa çıkmak.

Taslaklarla alaycı arasındaki farkı şu şekilde tanımlar:

Saplamalara karşı alay etmeyle ilgili hatırlanması gereken en önemli şey, alaycıların taslaklar gibidir, ancak alaycı nesneye karşı iddiada bulunursunuz, oysaki saplamalara karşı atılmayın.

Sahte sadece hem saplamalar hem de alaylar için kullanılan addır. Örneğin, taslaklar ve alaylar arasındaki ayrımı umursamıyorsanız.

Osherove'un taslaklar ve alaylar arasında ayrım yapma şekli, test için sahte olarak kullanılan herhangi bir sınıfın hem taslak hem de alay olabileceği anlamına gelir. Belirli bir test için hangisi, tamamen çekleri testinize nasıl yazdığınıza bağlıdır. 

  • Testiniz test edilen sınıftaki veya aslında sahte olan herhangi bir yerdeki değerleri kontrol ettiğinde, sahte bir saplama olarak kullanılmıştır. Teste tabi olan sınıfa, doğrudan çağrılar tarafından döndürülen değerler aracılığıyla ya da dolaylı olarak çağrıları sonucu yan etkilere (bazı durumlarda) neden olan değerler aracılığıyla, değerler sağladı.
  • Testiniz sahte değerleri kontrol ettiğinde, sahte olarak kullanılmıştır.

FakeX sınıfının saplama olarak kullanıldığı bir test örneği:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, cut.SomeProperty);

fake örneği, bir saplama olarak kullanılır, çünkü Assert, fake yöntemini hiç kullanmaz.

X test sınıfının sahte olarak kullanıldığı bir test örneği:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, fake.SomeProperty);

Bu durumda, Assertfake üzerindeki bir değeri kontrol eder ve bu sahtekarlığı sahte yapar.

Şimdi, elbette bu örnekler oldukça tartışılır, ancak bu ayrımda büyük değer görüyorum. Eşyalarınızı nasıl test ettiğiniz ve testinizin bağımlılıklarının nerede olduğu konusunda sizi bilgilendirir.

Osherove’a katılıyorum

saf bir bakım perspektifinden bakıldığında, testlerimde alay kullanmak, kullanmadıklarından daha fazla sorun yaratıyor. Bu benim deneyimim oldu, ama ben her zaman yeni bir şeyler öğreniyorum.

Sahte aleyhte iddia etmek, testlerinizi hiç test edilmeyen bir sınıfın uygulanmasına büyük ölçüde bağımlı kıldığından, kaçınmak istediğiniz bir şeydir. Bu, ActualClassUnderTest sınıfı için yapılan testlerin, ClassUsedAsMock için yapılan uygulama değiştiği için kırılmaya başlayabileceği anlamına gelir. Ve bu bana kötü bir koku veriyor. ActualClassUnderTest testleri, tercihen yalnızca ActualClassUnderTest değiştirildiğinde yapılmalıdır.

Sahte aleyhte iddiaların yazılmasının, özellikle alaycı tipte bir TDD abonesi olduğunuzda yaygın bir uygulama olduğunun farkındayım. Sanırım klasik kampta Martin Fowler ile sıkıca birlikteyim (Bkz. Martin Fowler'ın "Mocks Stubs değil" ) ve Osherove gibi etkileşimli testlerden kaçının (bu sadece sahtekarlığa karşı iddia ederek yapılabilir). .

Neden burada tanımlandığı gibi alaycılardan kaçınmanız gerektiğine dair eğlenceli bir okuma için "fowler mockist classicist" için google. Çok sayıda fikir bulacaksınız.

78
Marjan Venema

Taslakların ve alayların kullanımını göstermek için, Roy Osherove'in " Ünite Testi Sanatı " 'ya dayanan bir örnek de eklemek istiyorum.

Hayal edin, yalnızca günlükleri yazdırma işlevine sahip bir LogAnalyzer uygulamasına sahibiz. Yalnızca bir web servisiyle konuşmak zorunda değil, web servisi bir hata atarsa, LogAnalyzer'ın hatayı farklı bir dış bağımlılığa kaydetmesi ve e-posta ile web servis yöneticisine göndermesi gerekir.

LogAnalyzer içinde test etmek istediğimiz mantık:

if(fileName.Length<8)
{
 try
  {
    service.LogError("Filename too short:" + fileName);
  }
 catch (Exception e)
  {
    email.SendEmail("a","subject",e.Message);
  }
}

Web servisi bir istisna atarken LogAnalyzer'ın e-posta servisini doğru aradığını nasıl test edersiniz? İşte karşılaştığımız sorular:

  • Web servisini nasıl değiştirebiliriz?

  • Web servisinden bir istisnayı nasıl simüle edebiliriz, böylece e-posta servisine yapılan aramaları test edebiliriz.

  • E-posta servisinin doğru veya aranan olduğunu biliyoruz.

İlk iki soruyu web servisi için bir saplama kullanarak ile ele alabiliriz. Üçüncü sorunu çözmek için e-posta servisi için sahte bir nesne kullanın yapabiliriz.

Sahte, bir saplama veya taklitçiyi tanımlamak için kullanılabilecek genel bir terimdir. Testimizde iki sahte numaramız olacak. Bunlardan biri, doğru parametrelerin e-posta servisine gönderildiğini doğrulamak için kullanacağımız e-posta servisi sahte olacaktır. Diğeri, web hizmetinden atılan bir istisnayı simüle etmek için kullanacağımız bir saplama olacak. Bu bir saplama çünkü test sonucunu doğrulamak için, sadece testin doğru çalıştığından emin olmak için web servisini sahte kullanmayacağız. E-posta servisi sahtedir, çünkü doğru bir şekilde çağrıldığını iddia edeceğiz.

[TestFixture]
public class LogAnalyzer2Tests
{
[Test]
 public void Analyze_WebServiceThrows_SendsEmail()
 {
   StubService stubService = new StubService();
   stubService.ToThrow= new Exception("fake exception");
   MockEmailService mockEmail = new MockEmailService();

   LogAnalyzer2 log = new LogAnalyzer2();
   log.Service = stubService
   log.Email=mockEmail;
   string tooShortFileName="abc.ext";
   log.Analyze(tooShortFileName);

   Assert.AreEqual("a",mockEmail.To); //MOCKING USED
   Assert.AreEqual("fake exception",mockEmail.Body); //MOCKING USED
   Assert.AreEqual("subject",mockEmail.Subject);

 }
}
7
nanospeck

En çok oy alan cevabın da belirttiği gibi, Martin Fowler bu farklılıkları Mocks Arubit Stubs ve özellikle Mocks ve Stubs alt başlığında tartışmaktadır, bu yüzden bu makaleyi mutlaka okuyun.

Nasıl'a odaklanmak yerine, bu şeyler farklıdır, bence neden 'e odaklanmak daha aydınlatıcıdır _ bunlar farklı kavramlardır. Her biri farklı bir amaç için var.

Sahte

A sahte "doğal" davranan, ancak "gerçek" olmayan bir uygulamadır. Bunlar bulanık kavramlardır ve bu yüzden farklı insanlar olayları neyin sahte kıldığına dair farklı anlayışlara sahiptir.

Sahte örnek bir bellek içi veritabanıdır (örneğin, :memory: store ile sqlite kullanarak). Bunu asla üretim için kullanmazsınız (veriler kalıcı olmadığı için), ancak bir test ortamında kullanmak için mükemmel bir veri tabanıdır. Aynı zamanda "gerçek" bir veritabanından çok daha hafif.

Başka bir örnek olarak, belki üretimde bir çeşit nesne deposu (örneğin Amazon S3) kullanırsınız, ancak bir testte nesneleri diskteki dosyalara kaydedebilirsiniz; o zaman "diske kaydet" uygulamanız sahte olacaktır. (Ya da bir disk içi dosya sistemi kullanarak "diske kaydet" işlemini bile taklit edebilirsiniz.)

Üçüncü bir örnek olarak, önbellek API'sı sağlayan bir nesne hayal edin; doğru arayüzü uygulayan ancak hiç önbellekleme yapmayan ancak her zaman bir önbellek döndüren bir nesne, bir tür sahte olacaktır.

Sahte olmanın amacı, test edilen sistemin davranışını etkilemek için değil, ancak testin uygulamayı basitleştirin'ı (gereksiz ya da ağır ağırlıkları kaldırarak) bağımlılıklar).

Koçanları

A stub "doğal olmayan" davranan bir uygulamadır. Belirli çıkışlara sahip belirli girişlere yanıt vermek için önceden yapılandırılmıştır (genellikle test kurulumu ile).

Bir saplamanın amacı, sisteminizi belirli bir duruma test ettirmektir. Örneğin, REST API ile etkileşime giren bazı kodlar için bir test yazıyorsanız, bir API ile saplama REST API her zaman bir hazır yanıt döndürür veya belirli bir hatayı içeren bir API isteğine yanıt verir. Bu şekilde, sistemin bu durumlara nasıl tepki gösterdiği hakkında iddialarda bulunan testleri yazabilirsiniz; Örneğin, API bir 404 hatası verirse kullanıcılarınızın aldığı yanıtı test etme.

Bir saplama genellikle yalnızca yanıt vermesini söylediğiniz etkileşimlere tam olarak yanıt vermek için uygulanır. Ancak bir saplama yapan temel özellik amaç: saplama, test durumunuzu ayarlamaktan ibarettir.

Mocks

A mock bir saplamaya benzer, ancak doğrulama eklenir. Bir alayın amacı, test edilen sisteminizin bağımlılıkla nasıl etkileşime girdiğiyle ilgili iddialarda bulunmaktır .

Örneğin, bir web sitesine dosya yükleyen bir sistem için bir test yazıyorsanız, bir dosyayı kabul eden ve yüklenen dosyanın doğru olduğunu iddia etmek için kullanabileceğiniz bir sahte oluşturabilirsiniz. Veya daha küçük bir ölçekte, test edilen sistemin sahte nesnenin belirli yöntemlerini çağırdığını doğrulamak için bir nesnenin sahte kullanılması yaygındır.

Alaylar, belirli bir sınama metodolojisi olan etkileşim testi öğesine bağlıdır. Sistem etkileşimleri yerine sistem durumu test etmeyi tercih edenler, eğer mümkünse alaycı kullanacaklardır.

Test iki katına

Sahteler, saplamalar ve alayların tümü test çiftleri kategorisine aittir. Test iki katı, testte kullandığınız herhangi bir nesne veya sistemdir yerine başka bir şey. Çoğu otomatik yazılım testi, bir ya da başka türden test çiftlerinin kullanılmasını içerir. Diğer bazı test çiftleri arasında kukla değerler , casuslar ve G/Ç kara delikler .

5
Daniel Pryden

Testleri anlamlı kılma meselesi. Testin iki nesne arasındaki ilişkiyi tanımlamasını istersem bir Mock beklentileri belirledim. Beni testteki ilginç davranışa götürecek bir destekleyici nesne ayarlıyorsam dönüş değerlerini saplarım.

5
Steve Freeman

Arrange-Act-Assert'e aşina iseniz, o zaman taslak ve alay arasındaki farkı sizin için yararlı olabilecek açıklamanın bir yolu da taslakların giriş durumunu düzenlemek için olduğu gibi düzenleme bölümüne aittir. aleyhindeki bölüm, aleyhindeki sonuçları iddia etmek için olduğu gibi.

Aptallar hiçbir şey yapmazlar. Bunlar sadece parametre listelerini doldurmak içindir, böylece tanımsız ya da boş hata alamazsınız. Ayrıca, yazım denetleyicisini kesinlikle yazılmış dillerde de karşılayabilir, böylece derlemenize ve çalıştırmanıza izin verilebilir.

2
Sammi

Üzerinde durduğun şey a mock object olarak adlandırılmış ve test çalışmasına yeni yardım eden her şey a stub .

1

Aşağıdaki kaynaktan çok şey öğrendim, Robert C. Martin (Bob Amca).

Temiz Kod Blogundaki Küçük Avcı

Arasındaki farkları ve inceliklerini açıklar

  • mankenleri
  • test iki katına
  • koçanları
  • casuslar
  • (gerçek) alay
  • sahte

Ayrıca Martin Fowler'tan bahseder ve biraz yazılım test geçmişini açıklar.

Hiçbir şekilde bu soruyu bu bağlantıyla doğru bir cevap olarak cevaplamayı düşünmüyorum. Bununla birlikte, alaycı ve casusluk kavramlarını daha iyi anlamama yardımcı oldu, bu yüzden daha fazla insana yardımcı olacağını umarak cevap veriyorum.

1
Erik

stub ve sahte , giriş parametrelerine göre tepkilerini değiştirebilecekleri nesnelerdir. aralarındaki temel fark, Sahte'nin gerçek dünyadaki bir uygulamaya saplamadan daha yakın olduğudur. Saplamalar, beklenen bir talebe temel olarak kodlanmış cevaplar içerir. Bir örnek görelim: 

public class MyUnitTest {

 @Test
 public void testConcatenate() {
  StubDependency stubDependency = new StubDependency();
  int result = stubDependency.toNumber("one", "two");
  assertEquals("onetwo", result);
 }
}

public class StubDependency() {
 public int toNumber(string param) {
  if (param == “one”) {
   return 1;
  }
  if (param == “two”) {
   return 2;
  }
 }
}

A Sahte sahte ve taslaklardan bir adım. Mocks, taslaklarla aynı işlevselliği sağlar ancak daha karmaşıktır. API'lerinde hangi sıradaki yöntemlerin çağrılması gerektiğini belirleyen kurallar olabilir. Çoğu sahte, bir yöntemin kaç kez çağrıldığını izleyebilir ve bu bilgilere dayanarak tepki verebilir. Mocks genellikle her çağrının içeriğini bilir ve farklı durumlarda farklı tepkiler verebilir. Bu nedenle, alaycılar, alay ettikleri sınıfın bir miktar bilgisini gerektirir. bir saplama, genellikle bir yöntemin kaç kez çağrıldığını veya hangi sıralarla bir metotun çağrıldığını takip edemez. Bir alay gibi görünüyor:

public class MockADependency {

 private int ShouldCallTwice;
 private boolean ShouldCallAtEnd;
 private boolean ShouldCallFirst;

 public int StringToInteger(String s) {
  if (s == "abc") {
   return 1;
  }
  if (s == "xyz") {
   return 2;
  }
  return 0;
 }

 public void ShouldCallFirst() {
  if ((ShouldCallTwice > 0) || ShouldCallAtEnd)
   throw new AssertionException("ShouldCallFirst not first thod called");
  ShouldCallFirst = true;
 }

 public int ShouldCallTwice(string s) {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallTwice called before ShouldCallFirst");
  if (ShouldCallAtEnd)
   throw new AssertionException("ShouldCallTwice called after ShouldCallAtEnd");
  if (ShouldCallTwice >= 2)
   throw new AssertionException("ShouldCallTwice called more than twice");
  ShouldCallTwice++;
  return StringToInteger(s);
 }

 public void ShouldCallAtEnd() {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallAtEnd called before ShouldCallFirst");
  if (ShouldCallTwice != 2) throw new AssertionException("ShouldCallTwice not called twice");
  ShouldCallAtEnd = true;
 }

}