Hiçbir istisna atılmadığını nasıl test edebilirim?


238

Bunu yapmanın bir yolu olacağını biliyorum:

@Test
public void foo(){
   try{
      //execute code that you expect not to throw Exceptions.
   }
   catch(Exception e){
      fail("Should not have thrown any exception");
   }
}

Bunu yapmanın daha temiz bir yolu var mı? (Muhtemelen Junit'in @Rulemi kullanılıyor ?)


10
Bir JUnit testinin, beklenen bir istisna dışında herhangi bir istisna atarsa ​​başarısız olduğu değerlendirilir. Genellikle herhangi bir istisna beklenmez.
Raedwald

JUnit'te hata ve hata arasında bir ayrım yok mu? Birincisi testin başarısız olduğu, ikincisi beklenmedik bir şey olduğu anlamına gelir.
Vituel

Yanıtlar:


198

Buna yanlış yaklaşıyorsun. Sadece işlevselliğinizi test edin: bir istisna atılırsa test otomatik olarak başarısız olur. Herhangi bir istisna yapılmazsa, testlerinizin hepsi yeşile döner.

Bu sorunun zaman zaman ilgisini çektiğini fark ettim, bu yüzden biraz genişleyeceğim.

Birim testinin arka planı

Birim testi yaparken, kendiniz için bir iş birimi olarak neyi düşündüğünüzü tanımlamak önemlidir. Temel olarak: kod tabanınızın tek bir işlevsellik parçasını temsil eden birden çok yöntem veya sınıf içerebilen veya içermeyen bir çıkarma.

Veya Roy Osherove'nin Birim Testi sanatı, 2. Baskı'da tanımlandığı gibi , sayfa 11:

Bir birim test sonra kontroller birimin tek bir son sonuç hakkında bazı varsayım işin birimi, test edilen çağırır ve kod otomatik bir parçasıdır. Birim testi neredeyse her zaman birim test çerçevesi kullanılarak yazılır. Kolayca yazılabilir ve hızlı çalışır. Güvenilir, okunabilir ve bakımı kolaydır. Üretim kodu değişmediği sürece sonuçlarında tutarlıdır.

Fark edilmesi gereken önemli olan, bir iş biriminin genellikle sadece bir yöntem değil, aynı zamanda en temel düzeyde bir yöntem olmasıdır ve bundan sonra diğer iş birimleri tarafından kapsüllenir.

resim açıklamasını buraya girin

İdeal olarak, her bir ayrı iş birimi için bir test yönteminiz olmalıdır, böylece her şeyin nerede ters gittiğini hemen görebilirsiniz. Bu örnekte getUserById()kullanıcı döndürecek temel bir yöntem vardır ve toplam 3 birim çalışma vardır.

İlk iş birimi, geçerli ve geçersiz giriş durumunda geçerli bir kullanıcının iade edilip edilmediğini test etmelidir.
Veri kaynağı tarafından atılan tüm istisnalar burada ele alınmalıdır: kullanıcı yoksa, kullanıcı bulunamadığında bir istisnanın atıldığını gösteren bir test olmalıdır. Buna bir örnek ek açıklama IllegalArgumentExceptionile yakalanmış olabilir @Test(expected = IllegalArgumentException.class).

Bu temel iş birimi için tüm kullanımlarınızı ele aldıktan sonra, bir seviye yukarı çıkmış olursunuz. Burada tam olarak aynı şeyi yaparsınız, ancak yalnızca geçerli olanın hemen altındaki düzeyden gelen istisnaları ele alırsınız. Bu, test kodunuzu iyi yapılandırılmış tutar ve her yerde atlamak yerine, işlerin yanlış gittiğini bulmak için mimariyi hızla çalıştırmanıza olanak tanır.

Bir testin geçerli ve hatalı girdisini işleme

Bu noktada, bu istisnaları nasıl ele alacağımız açık olmalıdır. 2 tür giriş vardır: geçerli giriş ve hatalı giriş (giriş kesin anlamda geçerlidir, ancak doğru değildir).

Geçerli girdilerle çalıştığınızda, yazdığınız herhangi bir testin işe yarayacağına ilişkin örtük beklentiyi belirlersiniz.

Böyle bir yöntem çağrısı şöyle olabilir: existingUserById_ShouldReturn_UserObject. Bu yöntem başarısız olursa (örn: bir istisna atılır), bir şeyin yanlış gittiğini bilirsiniz ve kazmaya başlayabilirsiniz.

Hatalı girdiyi nonExistingUserById_ShouldThrow_IllegalArgumentExceptionkullanan ve bir istisna bekleyen başka bir test ( ) ekleyerek , yönteminizin yanlış girdi ile yapması gerekeni yapıp yapmadığını görebilirsiniz.

TL; DR

Testinizde iki şey yapmaya çalışıyordunuz: geçerli ve hatalı giriş olup olmadığını kontrol edin. Bunu, her birinin bir şey yaptığı iki yönteme bölerek, çok daha net testlere ve işlerin yanlış gittiği yere daha iyi bir genel bakışa sahip olacaksınız.

Katmanlı iş birimini akılda tutarak, hiyerarşide daha yüksek bir katman için ihtiyacınız olan test miktarını da azaltabilirsiniz, çünkü alt katmanlarda yanlış giden her şeyi hesaba katmanız gerekmez: geçerli katmanın altındaki katmanlar bağımlılıklarınızın işe yaradığına dair sanal bir garantidir ve bir şeyler ters giderse, geçerli katmanınızdadır (alt katmanların herhangi bir hata atmadığını varsayarsak).


2
Mesele şu ki TDD'ye çalışıyorum ve kullandığım ortak çalışanlardan biri bir istisna atıyor. Bu yüzden işbirlikçi tarafından atılan istisnayı tükettiğimi test etmem gerekiyor
Ankit Dhingra

6
İşlevselliğinizin bir istisnanın işlenmesine bağlı olduğunu mu söylüyorsunuz? Bu bir kod kokusu: sorunları yakalamanıza izin vermek için istisnalar var; akış kontrolü için kullanılmazlar. Bir istisnanın atılması gereken bir senaryoyu test etmek istiyorsanız expectedek açıklamayı kullanmalısınız . Kodunuzun başarısız olduğu bir senaryoyu test etmek ve hatanın doğru bir şekilde işlenip işlenmediğini görmek istiyorsanız: expectedçözümlenip çözümlenmediğini belirlemek için varsayımlar kullanın ve belki kullanın.
Jeroen Vannevel

Mesele şu ki, ortak çalışanda meydana gelen istisnadan kurtulamıyorum ve tüm yaptığım sadece bir log.debug ("Hata mesajı") kullanarak sorunu günlüğe kaydetmektir. Dolayısıyla, yakalama bloğunun bir parçası olarak muhtemelen iddia edebileceğim herhangi bir yan etki yoktur.
Ankit Dhingra

5
@JeroenVannevel, bir istisnanın atılmasına neden olan bir hata durumunun düzgün işlendiğini sınamak için tamamen geçerlidir.
Thorbjørn Ravn Andersen

1
@dpk evet yapabilirsin. Testinize eklersiniz throws IllegalArgumentException. Sonunda istediğiniz şey, bir istisna varsa testinizin kırmızıya dönüşmesidir. Öyleyse tahmin et? Yazmanıza gerek yok fail(). @Jeroen Vannevel'in yazdığı gibi: "bir istisna atılırsa test otomatik olarak başarısız olur."
Amedee Van Gasse

132

SonarQube'un "kalamar: S2699" kuralı yüzünden tökezledim: "Bu test senaryosuna en az bir iddia ekleyin."

Tek amacı istisnasız olarak geçmek olan basit bir test yaptım.

Şu basit kodu düşünün:

public class Printer {

    public static void printLine(final String line) {
        System.out.println(line);
    }
}

Bu yöntemi test etmek için ne tür bir iddia eklenebilir? Tabii, etrafında bir try-catch yapabilirsiniz, ama bu sadece kod şişmesi.

Çözüm, JUnit'in kendisinden geliyor.

Herhangi bir istisna atılmazsa ve bu davranışı açıkça göstermek istiyorsanız expected, aşağıdaki örnekte olduğu gibi eklemeniz yeterlidir :

@Test(expected = Test.None.class /* no exception expected */)
public void test_printLine() {
    Printer.printLine("line");
}

Test.None.class beklenen değer için varsayılan değerdir.


30
Bence bu en iyi cevap. Kabul edilen cevap harika ve yazar kod kokusunu işaret etmekte haklı. Ancak bu soruya gerçekten cevap vermedi.
HellishHeat

4
beklenen değer için varsayılan değerin Yok olduğunu belirtmek ilginçtir, bu yüzden sadece @Test ile yönteme açıklama eklemek yeterlidir.
oziomajnr


41

JUnit 5 (Jüpiter) istisna olup olmadığını / varlığını kontrol etmek için üç fonksiyon sağlar:

assertAll​()

İddia o tüm tedarikexecutables
  istisnalar atmıyoruz.

assertDoesNotThrow​()

Tedarik
  edilen executable/ yürütülmesinin herhangi bir istisna supplier
atmadığını iddia eder .

  Bu işlev JUnit 5.2.0'dan (29 Nisan 2018)
  beri kullanılabilir .

assertThrows​()

İddia sağlanan yerine getirilmesini executable
atar bir istisnası expectedType
  ve getiri istisna .

Misal

package test.mycompany.myapp.mymodule;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

class MyClassTest {

    @Test
    void when_string_has_been_constructed_then_myFunction_does_not_throw() {
        String myString = "this string has been constructed";
        assertAll(() -> MyClass.myFunction(myString));
    }

    @Test
    void when_string_has_been_constructed_then_myFunction_does_not_throw__junit_v520() {
        String myString = "this string has been constructed";
        assertDoesNotThrow(() -> MyClass.myFunction(myString));
    }

    @Test
    void when_string_is_null_then_myFunction_throws_IllegalArgumentException() {
        String myString = null;
        assertThrows(
            IllegalArgumentException.class,
            () -> MyClass.myFunction(myString));
    }

}

1
Bu şimdi en iyi cevap. Diğer cevaplar JUnit
Tejesh Raut

29

Java 8 bunu çok kolaylaştırır ve Kotlin / Scala iki katına çıkar.

Küçük bir yardımcı sınıf yazabiliriz

class MyAssertions{
  public static void assertDoesNotThrow(FailingRunnable action){
    try{
      action.run()
    }
    catch(Exception ex){
      throw new Error("expected action not to throw, but it did!", ex)
    }
  }
}

@FunctionalInterface interface FailingRunnable { void run() throws Exception }

ve sonra kodunuz basitçe:

@Test
public void foo(){
  MyAssertions.assertDoesNotThrow(() -> {
    //execute code that you expect not to throw Exceptions.
  }
}

Java-8'e erişiminiz yoksa, acı verici eski bir java tesisi kullanırdım: aribitrary kod blokları ve basit bir yorum

//setup
Component component = new Component();

//act
configure(component);

//assert 
/*assert does not throw*/{
  component.doSomething();
}

Ve son olarak, yakın zamanda aşık olduğum bir dil olan kotlin ile:

fun (() -> Any?).shouldNotThrow() 
    = try { invoke() } catch (ex : Exception){ throw Error("expected not to throw!", ex) }

@Test fun `when foo happens should not throw`(){

  //...

  { /*code that shouldn't throw*/ }.shouldNotThrow()
}

Bunu tam olarak nasıl ifade etmek istediğinizle uğraşmak için çok fazla alan olmasına rağmen, ben her zaman akıcı iddiaların hayranıydım .


ilişkin

Buna yanlış yaklaşıyorsun. Sadece işlevselliğinizi test edin: bir istisna atılırsa test otomatik olarak başarısız olur. Herhangi bir istisna yapılmazsa, testlerinizin hepsi yeşile döner.

Bu prensipte doğrudur ancak sonuç olarak yanlıştır.

Java, kontrol akışı için istisnalara izin verir. Bu gibi API'lerde JRE çalışma zamanı kendisi tarafından yapılır Double.parseDoublebir aracılığı NumberFormatExceptionve Paths.geta aracılığıInvalidPathException .

Number dizelerini doğrulayan Double.ParseDouble, belki de Regex, belki elle yazılmış bir ayrıştırıcı veya belki de bir çiftin aralığını belirli bir şeye sınırlayan diğer alan adı kurallarını gömen bir şey yazdığınız göz önüne alındığında, bunu nasıl test edersiniz? bileşen? Açık bir test ortaya çıkan dize ayrıştırıldığında, bir istisna atılmış olduğunu iddia etmek olacağını düşünüyorum. Bu testi yukarıdaki assertDoesNotThrowveya /*comment*/{code}bloğu kullanarak yazardım. Gibi bir şey

@Test public void given_validator_accepts_string_result_should_be_interpretable_by_doubleParseDouble(){
  //setup
  String input = "12.34E+26" //a string double with domain significance

  //act
  boolean isValid = component.validate(input)

  //assert -- using the library 'assertJ', my personal favourite 
  assertThat(isValid).describedAs(input + " was considered valid by component").isTrue();
  assertDoesNotThrow(() -> Double.parseDouble(input));
}

Ayrıca, bu testi inputkullanarak Theoriesveya parametrelerini parametreleştirmenizi tavsiye ederim, Parameterizedböylece bu testi diğer girişler için daha kolay kullanabilirsiniz. Alternatif olarak, egzotik olmak istiyorsanız, bir test oluşturma aracı (ve bu ) için gidebilirsiniz . TestNG'nin parametreli testler için daha iyi desteği vardır.

Özellikle kabul edilemez bulduğum şey kullanma önerisidir @Test(expectedException=IllegalArgumentException.class), bu istisna tehlikeli derecede geniştir . Kodunuz, testin yapıcısındaki bileşenin sahip olacağı şekilde değişirseif(constructorArgument <= 0) throw IllegalArgumentException() ve testiniz bu argüman için 0 sağlıyordu çünkü uygun olduğu için - ve bu çok yaygındır, çünkü iyi test verileri oluşturmak şaşırtıcı derecede zor bir sorundur--, o zaman testiniz hiçbir şey test etmese bile yeşil çubuk olacaktır. Böyle bir test işe yaramazdan daha kötüdür.


2
(beklenen istisna kullanımı ile ilgili) JUnit 4.13'ten bu yana Assert.assertThrows, bazı kodların istisna verip vermediğini kontrol etmek için kullanabilirsiniz .
MageWind

22

Kodunuzdaki tüm hataları yakalayacak kadar şanssızsanız. Aptalca yapabilirsin

class DumpTest {
    Exception ex;
    @Test
    public void testWhatEver() {
        try {
            thisShouldThrowError();
        } catch (Exception e) {
            ex = e;
        }
        assertEquals(null,ex);
    }
}

1
Test etmeden önce sadece küçük bir öneri Exception exolmalı = null;.
Denees

4
Bu harika bir çözüm değil. İstisna atmaması gereken yöntem işe yaramazsa, yararlı bir hata mesajı almazsınız. İstisna atmaması gereken yöntemi çağırmanız ve bunun dönüş değerini (veya istisnayı günlüğe kaydetme gibi yan etkileri) test etmeniz yeterlidir. Daha sonra beklenmedik bir şekilde bir istisna atarsa, test başarısız olur.
NamshubWriter

3
Ya da Assert.fail () yöntemini yakalama, daha kolay ve daha güzel IMO'ya koyun.
isaac.hazan

Evet ben size katılıyorum. Bir başka yol da @Test yönteminin üstüne bir açıklama eklemek (beklenen = InvalidRequestException.class)
Ben Tennyson

Yanlış yazımınız kafa karıştırıcı: thisShouldThroughError -> thisShouldThrowError .
Oscar Bravo


7

Bu yazı şu anda 6 yaşında olmasına rağmen, Junit dünyasında çok şey değişti. Junit5 ile artık

org.junit.jupiter.api.Assertions.assertDoesNotThrow()

Ör:

public void thisMethodDoesNotThrowException(){
   System.out.println("Hello There");
}

@Test
public void test_thisMethodDoesNotThrowException(){
  org.junit.jupiter.api.Assertions.assertDoesNotThrow(
      ()-> thisMethodDoesNotThrowException()
    );
}

Umarım yeni Junit5 sürümünü kullanan insanlara yardımcı olur


Burada somut istisna sınıfını belirtmenin bir yolu olsaydı. Bu iç yapmak zorunda Awaitility's untilAsserted(ThrowingRunnable assertion). Test altındaki sistem şu anda sağladığım ThrowingRunnable üzerinde belirli bir istisna atıyor, ancak bunu yapmayı bırakana kadar biraz zaman vermek istiyorum. Ancak farklı bir istisna atarsa ​​testin anında başarısız olmasını isterim.
Ubeogesh

1

Test hedefinizin istisnayı kullanıp kullanmadığını test etmek istiyorsanız. Testi şu şekilde bırakın (jMock2 kullanarak sahte ortak çalışan):

@Test
public void consumesAndLogsExceptions() throws Exception {

    context.checking(new Expectations() {
        {
            oneOf(collaborator).doSth();
            will(throwException(new NullPointerException()));
        }
    });

    target.doSth();
 }

Hedefiniz atılan istisnayı tüketirse test geçer, aksi takdirde test başarısız olur.

İstisna tüketim mantığınızı test etmek istiyorsanız, işler daha karmaşık hale gelir. Tüketimin alay konusu olabilecek bir ortak çalışana devredilmesini öneriyorum. Bu nedenle test şöyle olabilir:

@Test
public void consumesAndLogsExceptions() throws Exception {
    Exception e = new NullPointerException();
    context.checking(new Expectations() {
        {
            allowing(collaborator).doSth();
            will(throwException(e));

            oneOf(consumer).consume(e);
        }
    });

    target.doSth();
 }

Ancak bazen sadece günlüğe kaydetmek istiyorsanız aşırı tasarlanmış. Bu durumda, bu makale ( http://java.dzone.com/articles/monitoring-declarative-transac , http://blog.novoj.net/2008/09/20/testing-aspect-pointcuts-is-there -an-easy-way / ) bu durumda tdd'de ısrar ederseniz yardımcı olabilir.


1

AssertNull kullan (...)

@Test
public void foo() {
    try {
        //execute code that you expect not to throw Exceptions.
    } catch (Exception e){
        assertNull(e);
    }
}

6
Bunun yanıltıcı olduğunu söyleyebilirim. Yakalama bloğuna asla ulaşılamaz, bu yüzden de assertNullhiçbir zaman yürütülmez. Bununla birlikte, hızlı okuyucu, atmayan davayı gerçekten doğrulayan bir iddianın yapıldığı izlenimini edinir. Başka bir deyişle: catch bloğuna ulaşılırsa, istisna her zaman null değildir - bu nedenle bir basit ile değiştirilebilir fail.
Andreas

Gerçekten de yanıltıcı ..... ama beklemek ... ah Anlıyorum ... assertNull(e)başarısız olarak belirtildiği gibi, testi rapor eder eolamaz nulliçinde catch... Mike bu sadece tuhaf programlama olduğu bloğun: - /. .. evet en azından fail()Andreas'ın dediği gibi kullanın
Julien

1

Bir kural oluşturarak istisnanın atılmamasını bekleyebilirsiniz.

@Rule
public ExpectedException expectedException = ExpectedException.none();

ExpectedExceptions, atılan istisnaları iddia etmek için kullanılır. Sağladığınız kod yalnızca kuralı başlatmaktır, böylece iddialarla ilgili gereksinimlerinizi ekleyebilirsiniz. Bu kodun kendisi hiçbir değer katmaz. Javadoc ayrıca şunu da ifade eder: "/ ** * * atılmak için herhangi bir istisna beklemeyen bir {@linkplain TestRule kuralı} döndürür (bu kural olmadan davranışla aynıdır). .
Pim Hazebroek

Size katılıyorum ve bu şekilde kullanmazdım, ancak herhangi bir istisna atılmadığını iddia etmek mümkündür. Test başarılı olursa, istisnanın atılmadığını söylemek için yeterli olmalıdır, ancak diğer yandan bir soru varsa buna ihtiyaç duyulmalıdır. Ve nadiren ama yine de bazen görünür olması iyidir. Kod ve koşullar değiştiğinde ve belirli bir son durum için testimiz yoksa ne olur?
LazerBanana

Beklenen istisna dışında bunu nasıl iddia edeceğinizi merak ediyorum. Ve evet, gereksinimler değişirse ve vidalandığınız belirli bir kenar durumu için testiniz yoksa ;-) her zaman tüm köşe kılıflarını kapsar.
Pim Hazebroek

ne demek istiyorsun? üzerinde iddiada bulunmazsınız, beklersiniz. Bu durumda, bir istisna beklemezsiniz. Ne hakkında olduğundan emin değilim.
LazerBanana

0

Bu en iyi yol olmayabilir, ancak test edilen kod bloğundan istisnanın atılmamasını kesinlikle sağlar.

import org.assertj.core.api.Assertions;
import org.junit.Test;

public class AssertionExample {

    @Test
    public void testNoException(){
        assertNoException();
    }    

    private void assertException(){
        Assertions.assertThatThrownBy(this::doNotThrowException).isInstanceOf(Exception.class);
    }

    private void assertNoException(){
        Assertions.assertThatThrownBy(() -> assertException()).isInstanceOf(AssertionError.class);
    }

    private void doNotThrowException(){
        //This method will never throw exception
    }
}

0

Bunu bir @Rule kullanarak yapabilir ve sonra reportMissingExceptionWithMessage yöntemini aşağıda gösterildiği gibi çağırabilirsiniz: Bu Scala kodudur.

resim açıklamasını buraya girin


1
private val? Bu dil nedir? Açıkça Java değil; p Ve lütfen, ekran görüntüsü olarak kod vermeyin, memnuniyetle karşılanmıyor.
Andremoniy

Scala olduğundan bahsettiğinizi görüyorum, ancak Java'da kolayca yapılabileceğini söylemek güçlü bir argüman değil, üzgünüm
Andremoniy

Seni rahatsız eden kısmı çıkardım. Görüntüyü de değiştirmeye çalışacağım. Henüz kod eklemek için nasıl anladım ..
Crenguta S

-1

Aşağıdakiler, işaretlenmiş veya işaretlenmemiş tüm istisnalar için testi geçemez:

@Test
public void testMyCode() {

    try {
        runMyTestCode();
    } catch (Throwable t) {
        throw new Error("fail!");
    }
}

-1

Junit'in iddialarına dayanarak kendi iddialarınızı oluşturabilirsiniz:

static void assertDoesNotThrow(Executable executable) {
    assertDoesNotThrow(executable, "must not throw");
}
static void assertDoesNotThrow(Executable executable, String message) {
    try {
        executable.execute();
    } catch (Throwable err) {
        fail(message);
    }
}

Ve test edin:

//the following will succeed
assertDoesNotThrow(()->methodMustNotThrow(1));
assertDoesNotThrow(()->methodMustNotThrow(1), "fail with specific message: facepalm");
//the following will fail
assertDoesNotThrow(()->methodMustNotThrow(2));
assertDoesNotThrow(()-> {throw new Exception("Hello world");}, "Fail: must not trow");

Genel olarak konuşursak, testi herhangi bir senaryoda, mantıklı olduğu herhangi bir yerde anında başarısız olma ("bla bla bla") olasılığı vardır. Örneğin, test durumunda herhangi bir şey atılırsa başarısız olmak için bir dene / yakala bloğunda kullanın:

try{methodMustNotThrow(1);}catch(Throwable e){fail("must not throw");}
//or
try{methodMustNotThrow(1);}catch(Throwable e){Assertions.fail("must not throw");}

Bu, test ettiğimiz yöntemin bir örneğidir, belirli koşullar altında başarısız olmaması gereken bir yöntemimiz olduğunu varsayarsak, başarısız olabilir:

void methodMustNotThrow(int x) throws Exception{
    if (x == 1) return;
    throw new Exception();
}

Yukarıdaki yöntem basit bir örnektir. Ancak bu, başarısızlığın çok açık olmadığı karmaşık durumlar için geçerlidir. İthalat var:

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import static org.junit.jupiter.api.Assertions.*;

Özel kod oluşturmayı içermeyen iddianın atılmadığını kontrol etmek için daha iyi bir seçenek var. @ Kural onlardan biri
Vargan

@Vargan JUnit Tarafından Tasarlandığı şekilde kendi iddialarınızı yaratma yöntemini özellikle kendi iddialarınızı oluşturmak amacıyla işaret ettim. JUnit, tasarım yoluyla, özellikle bu amaçla, kendi kurallarınızı yaratmanızı, JUnit'in davranışını henüz uygulanmayan iddialarla genişletmesini sağlar. Çünkü bu dünyada her şey uygulanmadı Bu iddialar, JUnit iddiası başarılı veya başarısız olmanın yanı sıra raporlama hataları açısından da aynı şekilde çalışır.
armagedescu
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.