Statik evrensel olarak ünite testi için “kötülük” mü ve öyleyse neden Resharper bunu tavsiye ediyor? [kapalı]


85

C #. NET'te statik olan birim sınama (sahte / saplama) bağımlılıklarının yalnızca 3 yolu olduğunu buldum:

Bunlardan ikisinin serbest olmadığı ve birinin sürüm 1.0'a vurmadığı göz önüne alındığında, statik şeyleri alay etmek çok kolay değil.

Bu statik yöntemler ve bu “kötülük” yapar (birim test anlamında)? Ve eğer öyleyse, neden yeniden gönderen statik, statik olabilecek bir şey yapmamı istiyor? (Yeniden yerleşmenin varsayılması aynı zamanda “kötü” değildir.)

Açıklama: Bir yöntemi test etmek istediğinizde ve bu yöntem farklı bir birim / sınıfta statik bir yöntem çağırdığında, senaryodan bahsediyorum . Sadece test altındaki yöntemi diğer ünite / sınıfındaki statik yöntemi çağırmak izin verirsen birim test çoğu tanımları ile, ardından da değil sen, birim test entegrasyon testi. (Faydalı, ancak birim testi değil.)


3
TheLQ: Yapabilirsin. Statik metotları test edememe konusunda konuştuğuna inanıyorum çünkü statik değişkenlere dokunduğu zaman çoğu zaman. Böylece testten sonra ve test arasındaki durumu değiştirir.

26
Şahsen ben çok fazla "birim" tanımı alıyor düşünüyorum. "Birim", "yalıtımlı olarak test etmek mantıklı olan en küçük birim" olmalıdır. Bu bir yöntem olabilir, bundan daha fazlası olabilir. Statik yöntemin bir durumu yoksa ve iyi bir şekilde test edilmişse, ikinci bir birim test çağrısına sahipse, bu bir sorun değildir (IMO).
mlk

10
“Şahsen bence“ ünite ”nin tanımını çok fazla alıyorsunuz.” Hayır, sadece standart kullanımla gidiyor ve sen kendi tanımını yapıyorsun.

7
"neden yeniden keskinleştirici statik, statik olabilecek bir şey yapmamı istiyor?" Resharper değil istemek bir şey yapmanız. Bu sadece değişikliğin mümkün olduğunu ve belki de POV kod analizinden istendiğini bildiriyor. Resharper kendi kararının yerine geçmez!
Adam Naylor

4
@ acidzombie24. Düzenli yöntemler statik durumu da değiştirebilir, böylece statik yöntemler kadar "kötü" olurlar. Durumu daha kısa bir yaşam döngüsüyle değiştirebilmeleri, onları daha da tehlikeli hale getiriyor. (Statik yöntemler lehine değilim, ancak durum değişikliği ile ilgili mesele normal yöntemlere de bir darbedir, hatta daha fazlası)
mike30

Yanıtlar:


105

Buradaki diğer cevaplara baktığımda, statik durumu tutan ya da yan etkilere neden olan statik yöntemler (bana gerçekten kötü bir fikir gibi ses çıkaran) ve sadece bir değer veren statik yöntemler arasında bir karışıklık olabileceğini düşünüyorum.

Halsiz ve hiçbir yan etkiye neden olmayan statik yöntemler, kolayca test edilebilir olmalıdır. Aslında, bu tür metotları "fakir bir adamın" işlevsel programlama biçimi olarak görüyorum; yöntemi bir nesneyi veya değeri verirseniz, bir nesneyi veya değeri döndürürsünüz. Daha fazlası değil. Bu tür yöntemlerin birim testini nasıl olumsuz etkileyeceğini anlamıyorum.


44
Statik yöntem test edilebilirdir, ancak statik metodu çağıran metotlar ne durumda? Arayanlar başka bir sınıftaysa, birim testi yapmak için ayrılması gereken bir bağımlılığı vardır.
Vaccano

22
@Vaccano: Ancak, durumu tutmayan ve yan etkisi olmayan statik yöntemler yazarsanız, bunlar zaten bir saplama için işlevsel olarak hemen hemen aynıdır.
Robert Harvey

20
Basit olanlar belki. Ancak daha karmaşık olanlar, istisnalar atmaya ve beklenmeyen çıktılar almaya başlayabilir (ve statik yöntem için birim testinde yakalanmalıdır, statik yöntemin arayanı için birim testinde olmamalıdır) veya en azından ben okuduğum literatüre inanmaya başladım.
Vaccano

21
@Vaccano: Çıktıları veya rastgele istisnaları olan statik bir yöntemin yan etkileri vardır.
Tikhon Jelvis,

10
@TikhonJelvis Robert edildi çıkışlar söz; ve "rastgele" istisnalar, bir yan etki olmamalı, esasen bir çıktı biçimidir. Mesele şu ki, ne zaman statik metodu çağıran bir metodu test ederseniz, o metodu ve potansiyel çıktısının tüm permütasyonlarını sararsınız ve metodu izole olarak test edemezsiniz.
Nicole,

26

Statik verilerin ve statik yöntemlerin kafasını karıştırıyor gibi görünüyorsunuz . Resharper, doğru hatırlıyorsam, eğer yapılabiliyorsa, privatebir sınıf statik içinde yöntemler yapılmasını önerir - bunun küçük bir performans avantajı sağladığını düşünüyorum. O değil statik "olabilir bir şey" yapma tavsiye!

Statik yöntemlerde yanlış olan bir şey yoktur ve test edilmesi kolaydır (statik verileri değiştirmedikleri sürece). Örneğin, statik yöntemlerle statik bir sınıfa aday olan bir Matematik kütüphanesini düşünün. Eğer böyle (bir yöntem) bir yönteminiz varsa:

public static long Square(int x)
{
    return x * x;
}

o zaman bu kesinlikle test edilebilir ve hiçbir yan etkisi yoktur. Sadece geçerken 20 tane 400 aldığınızı kontrol edin. Sorun değil.


4
Bu statik yöntemi çağıran başka bir sınıfınız olduğunda ne olur? Bu durumda basit görünüyor, ancak yukarıda sıralanan üç araçtan biri dışında izole edilemeyen bir bağımlılık. İzolasyon yapmazsanız, o zaman "birim test" değilsinizdir "entegrasyon testi" (çünkü farklı birimlerinizin ne kadar iyi bir şekilde "bütünleştiklerini" test ediyorsunuz.
Vaccano

3
Hiçbir şey olmuyor. Neden olsun ki? .NET framework statik yöntemlerle doludur. Bunları ünite testi yapmak istediğiniz hiçbir yöntemde kullanamayacağınızı mı söylüyorsunuz?
Dan Diplo

3
Kodunuz .NET Framework'ün üretim düzeyinde / kalitesinde ise, elbette devam edin. Ancak, birim testin bütün noktası, birimi izole olarak test ediyor. Ayrıca diğer birimlerdeki yöntemleri de çağırıyorsanız (statik veya başka bir durumda olabilirler), o zaman şimdi ünitenizi artı bağımlılıklarını test ediyorsunuz. Yararlı bir test değil, çoğu tanımı ile "birim test" değil demiyorum. (Çünkü teste tabi tutulan ünitenizi ve statik metodu olan üniteyi test ediyorsunuz).
Vaccano

10
Muhtemelen siz (veya başkaları) zaten statik metodu test etmiş ve etrafına çok daha fazla kod yazmadan önce (en azından beklendiği gibi) çalıştığını göstermiştir. Bir sonraki test ettiğiniz bölümde bir şey kırılırsa, ilk önce bakmanız gereken yer, önceden test ettiğiniz şeylerde değil.
cHao

6
@Vaccano Peki, Microsoft .NET Framework’ü nasıl test eder? Framework'teki sınıfların çoğu, System.Mathstatik fabrika yöntemlerinin bolluğundan bahsetmeyen diğer sınıflardaki (örneğin ) statik yöntemlere referans verir . modern diller için temel. Bunları yalıtılmış olarak test edebilirsiniz (çünkü bunlar genellikle deterministik olacaktır) ve sonra bunları sınıflarınızda endişelenmeden kullanabilirsiniz. Sorun değil!
Dan Diplo

18

Buradaki asıl soru "Bu kodu nasıl test ederim?" İse:

public class MyClass
{
   public void MethodToTest()
   {
       //... do something
       MyStaticClass.StaticMethod();
       //...more
   }
}

Ardından, kodu yeniden gözden geçirin ve her zamanki gibi statik sınıfa yapılan çağrıyı enjekte edin:

public class MyClass
{
   private readonly IExecutor _externalExecutor;
   public MyClass(_IExecutor executor)
   {
       _exeternalExecutor = executor;
   }

   public void MethodToTest()
   {
       //... do something
       _exetrnalExecutor.DoWork();
       //...more
   }
}

public class MyStaticClassExecutor : IExecutor
{
    public void DoWork()
    {
        MyStaticClass.StaticMethod();
    }
}

9
Neyse ki, kod okunabilirliği ve SO üzerinde KISS ile ilgili de sorularımız var :)
gbjbaanb

bir temsilci kolay olmaz ve aynı şeyi yapar mıydı?
jk.

@jk olabilir, ancak o zaman IoC konteyner kullanmak zor.
Güneşli

15

Statik mutlaka kötü değildir, ancak sahte / alaycı / taslaklarla birim testi söz konusu olduğunda seçeneklerinizi sınırlayabilirler.

Alay etmenin iki genel yaklaşımı vardır.

Bunlardan ilki (geleneksel - RhinoMocks, Moq, NMock2; manuel mock ve saplamalar da bu kampta), test dikişlerine ve bağımlılık enjeksiyonuna dayanıyor. Bazı statik kodları test ettiğiniz ve bağımlılıklarının olduğunu varsayalım. Bu şekilde tasarlanan kodda sıkça meydana gelen şey, statikin bağımlılığı tersine çevirerek kendi bağımlılıklarını yaratmasıdır . Yakında, bu şekilde tasarlanmış test altındaki alaylı arayüzleri koda sokamayacağınızı keşfedeceksiniz.

İkincisi (her şeyle alay - TypeMock, JustMock ve Moles tarafından uygulanır) .NET'in Profil API'sine güvenir . CIL talimatlarınızdan herhangi birini engelleyebilir ve kodunuzun bir kısmını sahte olarak değiştirebilir. Bu, TypeMock ve bu kamptaki diğer ürünlerin herhangi bir şeyle alay etmelerini sağlar: statik, mühürlü sınıflar, özel yöntemler - test edilebilir olarak tasarlanmamış şeyler.

İki düşünce okulu arasında devam eden bir tartışma var. Birincisi, test edilebilirlik için SOLID prensiplerini ve tasarımını takip edin (bu genellikle statiği kolayca takip etmeyi içerir). Diğeri, TypeMock'ı satın al ve endişelenme diyor.


14

Şuna bir bakın : "Statik Yöntemler Test Edilebilirliğe Ölümdür" . Argümanın kısa özeti:

Birim testi yapmak için, kodunuzun küçük bir parçasını almanız, bağımlılıklarını yeniden ölçmeniz ve izolasyonla test etmeniz gerekir. Statik yöntemlerle bu zordur, sadece küresel duruma erişmeleri durumunda değil, diğer statik yöntemleri çağırsalar bile.


32
Statik yöntemlerimde küresel durumu sürdürmüyorum ya da yan etkilere neden oluyorum, bu nedenle bu argüman benim için alakasız görünüyor. Bağladığınız makale, statik yöntemler basit bir işlemsel kodla sınırlıysa, "genel" bir şekilde (matematik işlevleri gibi) davranıyorsa, temeli olmayan kaygan bir eğim argümanı yapar.
Robert Harvey

4
@Robert Harvey - Başka bir sınıftan statik bir yöntem kullanan bir yöntemi nasıl test edersiniz (yani statik bir yönteme bağımlıdır). Eğer onu aramasına izin verirseniz, o zaman "ünite testi" değilsiniz "entegrasyon testi"
Vaccano

10
Sadece blog makalesini okumayın, aynı fikirde olmayan yorumları okuyun. Bir blog sadece bir fikirdür, gerçek değil.
Dan Diplo

8
@Vaccano: Yan etkisi olmayan veya dış durumu olmayan statik bir yöntem, işlevsel olarak bir değere eşittir. Bunu bilerek, bir tamsayı oluşturan bir yöntemi birim testinden farklı değildir. Bu, işlevsel programlamanın bize sağladığı en önemli fikirlerden biridir.
Steven Evers

3
Gömülü sistemleri engellemek ya da böcekleri uluslararası olaylar yaratma potansiyeli olan bir uygulama olan IMO, "test edilebilirlik" mimariyi yönlendirdiğinde, her şeyden önce en son köşe test metodolojilerini benimsemeden önce onu büktünüz. Ayrıca, XP'nin her konuşmaya hükmedilen her şeyin versiyonundan bıktım. XP bir otorite değil, bir endüstri. İşte, birim testinin orijinal ve mantıklı tanımı: python.net/crew/tbryan/UnitTestTalk/slide2.html
Erik Reppen

5

Nadiren kabul edilen basit gerçek, eğer bir sınıf başka bir sınıfa derleyici tarafından görülebilir bir bağımlılık içeriyorsa, o sınıftan ayrı olarak test edilemeyeceğidir . Bir sınamaya benzeyen bir şey yapabilir ve bir sınama olmuş gibi bir raporda görünebilirsiniz.

Ancak bir testin temel tanımlayıcı özelliklerine sahip olmayacaktır; şeyler yanlış olduğunda başarısız olmak, doğru olduklarında geçmek.

Bu, statik çağrılar, yapıcı çağrılar ve temel sınıftan veya arabirimden miras alınmayan yöntemlere veya alanlara yapılan başvurular için geçerlidir . Sınıf adı kodda görünüyorsa, derleyici tarafından görülebilir bir bağımlılıktır ve onsuz geçerli bir şekilde test edemezsiniz. Daha küçük herhangi bir parça, geçerli bir test edilebilir birim değildir . Bunu sanki herhangi bir girişimmiş gibi yapma denemesi, test çerçeveniz tarafından kullanılan ve "test geçti" demek için kullanılan XML'i yayınlamak için küçük bir yardımcı program yazmaktan daha anlamlı sonuçlara sahip olmayacaktır.

Buna göre, üç seçenek var:

  1. Bir sınıftan ve zor kodlanmış bağımlılıklardan oluşan birimin test edilmesi için birim testini tanımlar. Bu işe yarar, dairesel bağımlılıklardan kaçınmanızı sağlar.

  2. Testten sorumlu olduğunuz sınıflar arasında asla derleme zamanı bağımlılıkları oluşturmayın. Bu, sonuçta ortaya çıkan kod stiline aldırış etmemek kaydıyla çalışır.

  3. birim testi yapma, entegrasyon testi yerine. Hangi işe yarıyorsa, entegrasyon testi terimini kullanmak için ihtiyacınız olan başka bir şeyle çakışmaz.


3
Birim testinin tek bir sınıfın testi olduğunu söyleyen hiçbir şey yoktur. Tek bir ünitenin testi. Birim testlerinin tanımlayıcı özellikleri hızlı, tekrarlanabilir ve bağımsız olmalarıdır. Math.PiBir yönteme atıfta bulunmak, herhangi bir makul tanım gereği bir entegrasyon testi yapmaz.
sara

örn. seçenek 1. Bunun en iyi yaklaşım olduğuna katılıyorum, ancak diğer kişilerin terimleri farklı şekilde kullandıklarını bilmek yararlı olabilir (makul veya değil).
soru

4

Bunun iki yolu yok. ReSharper'ın önerileri ve C # 'nın çeşitli faydalı özellikleri, tüm kodunuz için izole edilmiş atomik birim testleri yazıyor olsanız sık sık kullanılmazdı.

Örneğin, statik bir yönteminiz varsa ve onu silmeniz gerekiyorsa, profil tabanlı bir yalıtım çerçevesi kullanmıyorsanız kullanamazsınız. Çağrı uyumlu bir geçici çözüm, lambda notasyonu kullanmak için yöntemin üstünü değiştirmektir. Örneğin:

ÖNCE:

    public static DBConnection ConnectToDB( string dbName, string connectionInfo ) {
    }

SONRA:

    public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) {
    };

İki çağrı uyumludur. Arayanların değişmesi gerekmez. Fonksiyonun gövdesi aynı kalır.

Ardından, Birim-Test kodunuzda, bu çağrıyı bu şekilde saptayabilirsiniz (Veritabanı sınıfında olduğu varsayılarak):

        Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }

İşlemi tamamladıktan sonra orijinal değerle değiştirmeye dikkat edin. Bunu bir deneme / nihayet yaparak veya birim test temizlemenizde, her testten sonra çağrılana aşağıdaki gibi kod yazabilirsiniz:

    [TestCleanup]
    public void Cleanup()
    {
        typeof(Database).TypeInitializer.Invoke(null, null);
    }

sınıfınızın statik başlatıcısını yeniden çağırır.

Lambda Funcs, normal statik yöntemler kadar destek açısından zengin değildir; bu nedenle, bu yaklaşım aşağıdaki istenmeyen yan etkilere sahiptir:

  1. Statik yöntem bir uzatma yöntemi ise, önce onu uzatma olmayan bir yönteme değiştirmeniz gerekir. Resharper bunu sizin için otomatik olarak yapabilir.
  2. Statik yöntemlerin veri türlerinden herhangi biri, Office için yerleşik bir birlikte çalışma derlemesi ise, yöntemi kaydırmanız, türü kaydırmanız veya 'nesne' türüne değiştirmeniz gerekir.
  3. Resharper'ın imza imzası yeniden düzenleme aracını artık kullanamazsınız.

Fakat diyelim ki, statikten tamamen kaçınmalısınız ve bunu bir örnek yöntemine dönüştürüyorsunuz. Yöntem sanal değilse ya da bir arabirimin parçası olarak uygulanmıyorsa, hala taklit edilemez.

Bu yüzden gerçekte, statik yöntemleri inat etmenin çaresini öneren herhangi biri, onlara örnek metotlar yapmaktır, sanal olmayan veya bir arayüzün parçası olan örnek metotlara karşı da olacaktır.

Peki neden C # statik yöntemlere sahip? Sanal olmayan örnek yöntemlere neden izin veriyor?

Bu "Özellikler" den herhangi birini kullanırsanız, o zaman izole edilmiş yöntemler oluşturamazsınız.

Peki onları ne zaman kullanıyorsun?

Bunları, hiç kimsenin taslak çıkarmak istememesini istediğiniz herhangi bir kod için kullanın. Bazı örnekler: String sınıfının Format () yöntemi, Console sınıfının WriteLine () yöntemi, Math sınıfının Cosh () yöntemidir.

Ve bir şey daha var .. Çoğu insan bunu umursamıyor, ancak dolaylı bir çağrının performansını merak ediyorsanız, bu örnek yöntemlerden kaçınmak için başka bir neden. Bunun bir performans vuruşu olduğu durumlar var. Bu yüzden ilk başta sanal olmayan yöntemler var.


3
  1. Kısmen bunun statik yöntemlerin çağırmak için örnek yöntemlerden daha hızlı olmaları olduğuna inanıyorum. (Çünkü bu mikro optimizasyon kokuyor tırnak) http://dotnetperls.com/static-method
  2. Size devlete ihtiyaç duymadığını söylüyor, bu nedenle herhangi bir yerden aranabilir ve birisinin ihtiyaç duyduğu tek şey buysa yükü kaldırabilir.
  3. Eğer benimle alay etmek istersem, sanırım genellikle bir arabirimde ilan edilen pratiktir.
  4. Bir arabirimde bildirilirse R #, statik hale getirmenizi önermez.
  5. Sanal olarak ilan edilirse, R # sizin de statik olmanızı önermez.
  6. Devleti (tarlaları) statik olarak tutmak her zaman dikkatli bir şekilde göz önünde bulundurulması gereken bir şeydir . Statik durum ve iplikler lityum ve su gibi karışır.

Bu öneriyi sağlayacak tek araç R # değildir. FxCop / MS Kod Analizi de aynı şeyi yapacaktır.

Genel olarak, eğer yöntem statikse, genel olarak olduğu gibi test edilebilir olması gerektiğini söylerdim. Bu, bazı tasarım değerlendirmeleri ve muhtemelen şu anda parmaklarımda yaptığımdan daha fazla tartışma getiriyor, bu yüzden aşağı oy ve yorumları sabırla bekliyoruz…;)


Arayüz üzerinde statik bir yöntem / nesne bildirir misiniz? (Ben öyle düşünmüyorum). Eter yolu, statik yönteminizin test edilen yöntem tarafından ne zaman çağrıldığına işaret ediyorum. Arayan farklı bir birimdeyse, statik yöntemin izole edilmesi gerekir. Yukarıda listelenen üç araçtan biriyle bunu yapmak çok zor.
Vaccano

1
Hayır, bir arabirimde statik olarak ilan edemezsiniz. Anlamsız olurdu.
MIA

3

Uzun zaman sonra hiç kimsenin gerçekten basit bir gerçek ifade etmediğini görüyorum. Resharper bana bir yöntemi statik hale getirebileceğimi söylerse, bu benim için çok büyük bir şey ifade ediyor, sesini bana söylediğini duyabiliyorum: "hey, bu mantık parçaları mevcut sınıfın SORUMLULUĞU değil, bu yüzden dışarıda kalmalı bazı yardımcı sınıf veya başka bir şeyde ".


2
Katılmıyorum. Resharper'ın statik bir şey yapabileceğimi söylediği çoğu zaman, sınıftaki iki veya daha fazla yöntem için ortak olan bir kod kodu olurdu ve bu yüzden kendi prosedürüne çıkardım. Bunu bir yardımcıya taşımak anlamsız bir karmaşıklık olurdu.
Loren Pechtel

2
Amacınızı yalnızca etki alanı çok basitse ve gelecekteki değişiklik için uygun değilse görebiliyorum. Farklı olarak, "anlamsız karmaşıklık" dediğiniz şey benim için iyi ve insan tarafından okunabilen bir tasarım. Var olması basit ve açık bir nedene sahip yardımcı bir sınıfa sahip olmak, bir şekilde SoC ve Tek Sorumluluk ilkelerinin "mantrası" dır. Ek olarak, bu yeni sınıfın ana sınıfa bağımlı hale geldiği göz önüne alındığında, bazı kamu üyelerini ortaya çıkarması gerekir ve doğal bir sonuç olarak, yalıtılmış bir şekilde test edilebilir hale gelir ve bir bağımlılık olarak davrandığında alay etmesi kolaydır.
g1ga

1
Soruyla ilgili değil.
Igby Largeman,

2

Statik yöntem başka bir yöntemin içinden çağrılıyorsa, böyle bir aramayı önlemek veya yerine koymak mümkün değildir. Bu, bu iki yöntemin tek bir Birim oluşturduğu anlamına gelir; Her çeşit birim testi, ikisini de test eder.

Ve bu statik yöntem İnternet ile konuşuyorsa, veritabanlarını birbirine bağlar, GUI açılır pencerelerini gösterir veya Birim testini tam bir karmaşaya dönüştürürse, sadece kolay bir iş yapmaz. Bu tür statik yöntemi çağıran bir yöntem, yeniden test edilmeden test edilemez, ünite test edilmesinden oldukça fazla faydalanabilecek çok sayıda hesaplama kodu vardır.


0

Resharper'ın size rehberlik ettiğine ve kurulduğu kodlama kurallarına uyduğuna inanıyorum. Resharper'ı kullandığımda ve bir yöntemin statik olması gerektiğini söylediğinde, herhangi bir örnek değişkenine etki etmeyen özel bir yöntem üzerinde olması şart.

Şimdi test edilebilirlik söz konusu olduğunda, bu senaryo, özel yöntemleri zaten test etmemeniz gerektiği gibi bir sorun olmamalı.

Halka açık statik yöntemlerin test edilebilirliğine gelince, statik yöntemler statik duruma dokunduğunda ünite testi zorlaşır. Şahsen bunu minimumda tutardım ve herhangi bir bağımlılığın bir test fikstürüyle kontrol edilebilecek olan metoda aktarıldığı durumlarda mümkün olduğunca saf fonksiyonlar gibi statik metotlar kullanırdım. Ancak bu bir tasarım kararıdır.


Başka bir sınıfta statik bir yöntem çağıran, test edilen (statik olmayan) bir yönteme sahip olduğunuzdan söz ediyorum. Bu bağımlılık sadece yukarıda listelenen üç araçtan biriyle izole edilebilir. İzolasyon yapmazsanız, birim testi değil entegrasyon testi yapıyorsunuzdur.
Vaccano

Örnek değişkenlerine doğal olarak erişmek, statik olamayacağı anlamına gelir. Statik bir yöntemin muhtemelen sahip olabileceği tek durum sınıf değişkenleridir ve olması gereken tek neden, bir singleton ile ilgileniyorsanız ve dolayısıyla başka seçeneğiniz yoksa.
Loren Pechtel
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.