Kural dışı durum iletimi JUnit Test ek açıklamasıyla nasıl iddia edebilirim?


315

@TestEk açıklama ile birkaç JUnit testi yazdım . Test yöntemim denetlenen bir özel durum atarsa ​​ve iletiyi özel durumla birlikte onaylamak istersem, JUnit @Testek açıklamasıyla bunu yapmanın bir yolu var mı? AFAIK, JUnit 4.7 bu özelliği sağlamıyor ancak gelecekteki sürümleri de sağlıyor mu? .NET'te ileti ve istisna sınıfını iddia edebileceğinizi biliyorum. Java dünyasında benzer bir özellik mi arıyorsunuz?

İstediğim bu:

@Test (expected = RuntimeException.class, message = "Employee ID is null")
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() {}

1
Şimdi biraz daha düşündüğüme göre ... Mesajı iddia etmenin iyi bir fikir olduğundan emin misin? Sorunuz beni junit kaynak koduna biraz kazdı ve bu özelliği kolayca ekledikleri anlaşılıyor. Yaptılar gerçeği değil , bana iyi bir uygulama olarak kabul edilmeyebilir düşündürüyor. Projenizde mesajı iddia etmek neden önemlidir?
c_maker

10
İyi bir soru. 15 satır kod içeren bir yöntemin aynı istisnayı 2 farklı yerden verdiğini unutmayın. Test durumlarımın sadece istisna sınıfını değil aynı zamanda içindeki mesajı da ileri sürmesi gerekiyor. İdeal bir dünyada, herhangi bir anormal davranışın kendine özgü bir istisnası olmalıdır.
Cshah

Bir yan not olarak - @expectedExceptionMessagePHPUnit'te ek açıklama var .
bancer

Yanıtlar:


535

@RuleEk açıklamayı şu şekilde kullanabilirsiniz ExpectedException:

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

@Test
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() throws Exception {
    expectedEx.expect(RuntimeException.class);
    expectedEx.expectMessage("Employee ID is null");

    // do something that should throw the exception...
    System.out.println("=======Starting Exception process=======");
    throw new NullPointerException("Employee ID is null");
}

ExpectedExceptionDokümanlardaki örneğin (şu anda) yanlış olduğuna dikkat edin - genel kurucu yoktur, bu yüzden kullanmanız gerekir ExpectedException.none().


1
Not: Benim expectMessageiçin boş bir dize olarak belirtildiğinde, ileti için karşılaştırma yapılmadı
redDevil

1
Benim için yararlı. Teşekkürler. Test yöntemi, throws RuntimeExceptionbir istisna atan bir kod ekledikten sonra olmalıdır . Yakalamayın ...
Bumbolt

5
Şahsen bunu kullanmak istemezdim çünkü küçük bir yöntem alt kümesi için alanlar oluşturmak kötü bir uygulamadır. Yanıt değil, JUnit'in tasarımıyla ilgili bir eleştiri. OP'nin varsayımsal çözümü, mevcut olsaydı çok daha iyi olurdu.
Sridhar Sarnobat

2
@redDevil: ExpectedMessage, hata iletisinin bu işlevde belirtilen ("hata iletisinin bir alt dizesi gibi) dizeyi" içerip içermediğini "denetler
tuan.dinh

3
dize parametresi ile expectMessage istisna mesajı kullanımı hamcrest Eşleştirici tam maç için, bir String.contains kontroller yaparfailure.expectMessage(CoreMatchers.equalTo(...))
Sivabalan

42

@RuleCevabı beğendim . Ancak, herhangi bir nedenle kuralları kullanmak istemiyorsanız. Üçüncü bir seçenek daha var.

@Test (expected = RuntimeException.class)
public void myTestMethod()
{
   try
   {
      //Run exception throwing operation here
   }
   catch(RuntimeException re)
   {
      String message = "Employee ID is null";
      assertEquals(message, re.getMessage());
      throw re;
    }
    fail("Employee Id Null exception did not throw!");
  }

32

Kullanmak zorunda @Test(expected=SomeException.class)mısın? İstisnanın gerçek mesajını savunmamız gerektiğinde, bunu yaparız.

@Test
public void myTestMethod()
{
  try
  {
    final Integer employeeId = null;
    new Employee(employeeId);
    fail("Should have thrown SomeException but did not!");
  }
  catch( final SomeException e )
  {
    final String msg = "Employee ID is null";
    assertEquals(msg, e.getMessage());
  }
}

6
Bir catch blok yazma ve bu içinde iddia kullanarak biliyorum ama daha iyi kod okunabilirliği için ek açıklamaları ile yapmak istiyorum.
Cshah

Ayrıca "doğru" şekilde yaptığınız gibi güzel bir mesaj alamazsınız.
NeplatnyUdaj

15
Deneme / yakalama sürümü ile sorun, şimdi JUnit sağladığını @Test(expected=...)ve ExpectedExceptionben sayısız vesilelerle birisi gördük ki çağrısına koymak unutmak fail()sonunda trybloğu . Kod incelemesi tarafından yakalanmazsa, testiniz yanlış pozitif olabilir ve her zaman geçebilir.
William Price

Bu yüzden tüm bu beyan edici şeyleri sevmiyorum. İstediğinize erişmeyi zorlaştırır.
Sridhar Sarnobat

30

JUnit 4.13'te şunları yapabilirsiniz:

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

...

@Test
void exceptionTesting() {
  IllegalArgumentException exception = assertThrows(
    IllegalArgumentException.class, 
    () -> { throw new IllegalArgumentException("a message"); }
  );

  assertEquals("a message", exception.getMessage());
}

Bu, JUnit 5'te de çalışır, ancak farklı ithalatlarla:

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

...

Bu çözüm gibi. JUnit 5'e
taşınmalı

Gaaaaaaaaa. 4.13 bugün itibariyle hala beta aşamasında mı (Güz, 2019)? Mvnrepository.com/artifact/junit/junit
granadaCoder

v4.13 artık beta durumunda değil (Ocak 2020'de yayınlandı)
Simon

11

Aslında, en iyi kullanım try / catch ile yapılır. Neden? Çünkü istisnayı beklediğiniz yeri kontrol edebilirsiniz.

Bu örneği düşünün:

@Test (expected = RuntimeException.class)
public void someTest() {
   // test preparation
   // actual test
}

Bir gün kod değiştirilirse ve test hazırlığı bir RuntimeException kurarsa ne olur? Bu durumda gerçek test test edilmez ve herhangi bir istisna atmasa bile test geçer.

Bu nedenle, ek açıklamaya güvenmekten çok try / catch kullanmak çok daha iyidir.


Ne yazık ki, bu da benim cevabım.
Sridhar Sarnobat

2
Kod değişikliklerine ilişkin kaygılar, küçük, permütasyona özgü test senaryoları ile hafifletilir. Bazen kaçınılmazdır ve catch / try yöntemine güvenmemiz gerekir, ancak bu sık sık gerçekleşirse, test senaryosu işlevlerimizi yazma şeklimizi gözden geçirmemiz gerekir.
luis.espinal

Bu, testiniz ve / veya kodunuzla ilgili bir sorundur. Genel bir RuntimeException beklemiyorsunuz, belirli bir istisna veya en azından belirli bir ileti bekliyorsunuz.
DennisK

RuntimeExceptionÖrnek olarak kullandım , bu istisnayı başka bir istisna ile değiştirin.
Krzysztof Cislo

8

Raystorm'un iyi bir cevabı vardı. Ben de Kurallar'ın büyük bir hayranı değilim. Benzer bir şey yapıyorum, ancak ek açıklamaların en büyük artılarından biri olan okunabilirliğe ve kullanılabilirliğe yardımcı olmak için aşağıdaki yardımcı programı oluşturuyorum.

Bu yardımcı program sınıfını ekleyin:

import org.junit.Assert;

public abstract class ExpectedRuntimeExceptionAsserter {

    private String expectedExceptionMessage;

    public ExpectedRuntimeExceptionAsserter(String expectedExceptionMessage) {
        this.expectedExceptionMessage = expectedExceptionMessage;
    }

    public final void run(){
        try{
            expectException();
            Assert.fail(String.format("Expected a RuntimeException '%s'", expectedExceptionMessage));
        } catch (RuntimeException e){
            Assert.assertEquals("RuntimeException caught, but unexpected message", expectedExceptionMessage, e.getMessage());
        }
    }

    protected abstract void expectException();

}

Sonra benim birim testi için, tek ihtiyacım bu kod:

@Test
public void verifyAnonymousUserCantAccessPrivilegedResourceTest(){
    new ExpectedRuntimeExceptionAsserter("anonymous user can't access privileged resource"){
        @Override
        protected void expectException() {
            throw new RuntimeException("anonymous user can't access privileged resource");
        }
    }.run(); //passes test; expected exception is caught, and this @Test returns normally as "Passed"
}

2

@Rule kullanılıyorsa, özel durum kümesi Test sınıfındaki tüm test yöntemlerine uygulanır.


2
Jesse Merriman yanıtı kullanılarak, kural dışı durum yalnızca beklenenEx.expect () ve beklenenEx.expectMessage () yöntemlerini çağıran test yöntemlerinde denetlenir. Diğer yöntemler beklenenEx = ExpectedException.none () tanımını kullanır, yani bir istisna beklenmez.
Egl

2

Junit ile istisnalar öne sürmenin yolunu hiç sevmedim. Ek açıklamada "beklenen" i kullanırsam, benim görüşüme göre "verilen, ne zaman, sonra" kalıbını ihlal ediyoruz, çünkü "o zaman" test tanımının en üstüne yerleştirilir.

Ayrıca, "@ Kural" kullanırsak, çok fazla kaynak kodu ile uğraşmak zorundayız. Bu nedenle, testleriniz için yeni kütüphaneler yükleyebiliyorsanız, AssertJ'e göz atmanızı öneririm (bu kütüphane şimdi SpringBoot ile birlikte geliyor)

Daha sonra "verilen / ne zaman / sonra" ilkelerini ihlal etmeyen bir test ve aşağıdakileri doğrulamak için AssertJ kullanılarak yapılır:

1 - İstediğimiz şey istisnadır. 2 - Ayrıca beklenen bir mesajı var

Şuna benzeyecek:

 @Test
void should_throwIllegalUse_when_idNotGiven() {

    //when
    final Throwable raisedException = catchThrowable(() -> getUserDAO.byId(null));

    //then
    assertThat(raisedException).isInstanceOf(IllegalArgumentException.class)
            .hasMessageContaining("Id to fetch is mandatory");
}

1

User64141'in cevabını seviyorum ama bunun daha genel olabileceğini buldum. İşte benim almam:

public abstract class ExpectedThrowableAsserter implements Runnable {

    private final Class<? extends Throwable> throwableClass;
    private final String expectedExceptionMessage;

    protected ExpectedThrowableAsserter(Class<? extends Throwable> throwableClass, String expectedExceptionMessage) {
        this.throwableClass = throwableClass;
        this.expectedExceptionMessage = expectedExceptionMessage;
    }

    public final void run() {
        try {
            expectException();
        } catch (Throwable e) {
            assertTrue(String.format("Caught unexpected %s", e.getClass().getSimpleName()), throwableClass.isInstance(e));
            assertEquals(String.format("%s caught, but unexpected message", throwableClass.getSimpleName()), expectedExceptionMessage, e.getMessage());
            return;
        }
        fail(String.format("Expected %s, but no exception was thrown.", throwableClass.getSimpleName()));
    }

    protected abstract void expectException();

}

"Fail" ifadesini try bloğu içinde bırakmanın ilgili onaylama istisnasının yakalanmasına neden olduğunu unutmayın; catch deyimi içinde return kullanılması bunu engeller.


0

Catch-istisna kitaplığını içe aktarın ve kullanın. ExpectedExceptionKural veya a'dan çok daha temiz try-catch.

Dokümanlarından örnek:

import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*;

// given: an empty list
List myList = new ArrayList();

// when: we try to get the first element of the list
catchException(myList).get(1);

// then: we expect an IndexOutOfBoundsException with message "Index: 1, Size: 0"
assertThat(caughtException(),
  allOf(
    instanceOf(IndexOutOfBoundsException.class),
    hasMessage("Index: 1, Size: 0"),
    hasNoCause()
  )
);

-2
@Test (expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "This is not allowed")
public void testInvalidValidation() throws Exception{
     //test code
}

Birisi bu cevabın neden -1 olduğunu anlamama yardımcı olabilir
aasha

Soru soruyor Junitama cevabınız veriyorTestNG
Huazhe Yin
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.