Nice programing

JUnit 테스트가 예외를 발생시키는 잘못된 양식?

nicepro 2020. 12. 29. 08:26
반응형

JUnit 테스트가 예외를 발생시키는 잘못된 양식?


저는 JUnit을 처음 접했고 예외 및 예외 처리에 대한 모범 사례가 무엇인지 잘 모릅니다.

예를 들어, IPAddress 클래스에 대한 테스트를 작성한다고 가정 해 보겠습니다. addr가 null 인 경우 InvalidIPAddressException을 발생시키는 생성자 IPAddress (String addr)가 있습니다. 인터넷 검색에서 알 수있는 한 널 매개 변수에 대한 테스트는 다음과 같습니다.

@Test
public void testNullParameter()
{
    try
    {
        IPAddress addr = new IPAddress(null);
        assertTrue(addr.getOctets() == null);
    }
    catch(InvalidIPAddressException e)
    {
        return;
    }

    fail("InvalidIPAddressException not thrown.");
}

이 경우 예외가 발생한다는 것을 알고 있기 때문에 try / catch가 의미가 있습니다.

하지만 이제 testValidIPAddress ()를 작성하려면 몇 가지 방법이 있습니다.

방법 # 1 :

@Test
public void testValidIPAddress() throws InvalidIPAddressException
{
    IPAddress addr = new IPAddress("127.0.0.1");
    byte[] octets = addr.getOctets();

    assertTrue(octets[0] == 127);
    assertTrue(octets[1] == 0);
    assertTrue(octets[2] == 0);
    assertTrue(octets[3] == 1);
}

방법 # 2 :

@Test
public void testValidIPAddress()
{
    try
    {
        IPAddress addr = new IPAddress("127.0.0.1");
        byte[] octets = addr.getOctets();

        assertTrue(octets[0] == 127);
        assertTrue(octets[1] == 0);
        assertTrue(octets[2] == 0);
        assertTrue(octets[3] == 1);
    }
    catch (InvalidIPAddressException e)
    {
        fail("InvalidIPAddressException: " + e.getMessage());
    }
}

JUnit에 예상치 못한 예외를 던지거나 직접 처리하는 것이 표준 관행입니까?

도와 주셔서 감사합니다.


사실, 예전 스타일의 예외 테스트예외발생 시키는 코드 주위에 try 블록을 감싼 다음 try 블록 fail()의 끝에 명령문 을 추가하는 것 입니다. 이 같은:

public void testNullParameter() {
    try {
        IPAddress addr = new IPAddress(null);
        fail("InvalidIPAddressException not thrown.");
    } catch(InvalidIPAddressException e) {
        assertNotNull(e.getMessage());
    }
}

이것은 당신이 쓴 것과 크게 다르지 않지만 :

  1. 당신 assertTrue(addr.getOctets() == null);은 쓸모가 없습니다.
  2. 의도와 구문은 IMO가 더 명확하므로 읽기가 더 쉽습니다.

그래도 이것은 약간 못 생겼습니다. 그러나 예외 테스트가 JUnit 4의 가장 큰 개선 사항 중 하나이기 때문에 JUnit 4가 구출되는 곳입니다. JUnit 4를 사용하면 이제 다음과 같이 테스트를 작성할 수 있습니다.

@Test (expected=InvalidIPAddressException.class) 
public void testNullParameter() throws InvalidIPAddressException {
    IPAddress addr = new IPAddress(null);
}

멋지지 않나요?

이제 실제 질문과 관련하여 예외가 발생하지 않을 것이라고 예상하지 않는다면, 확실히 1 번 방법을 선택하고 (보다 덜 장황하기 때문에) JUnit이 예외를 처리하고 예상대로 테스트에 실패하도록 할 것입니다.


For tests where I don't expect an exception, I don't bother to catch it. I let JUnit catch the exception (it does this reliably) and don't cater for it at all beyond declaring the throws cause (if required).

I note re. your first example that you're not making use of the @expected annotation viz.

@Test (expected=IndexOutOfBoundsException.class) public void elementAt() {
    int[] intArray = new int[10];

    int i = intArray[20]; // Should throw IndexOutOfBoundsException
  }

I use this for all tests that I'm testing for throwing exceptions. It's briefer than the equivalent catch/fail pattern that I had to use with Junit3.


Since JUnit 4.7 you have the possibility to use an ExpectedException rule and you should use it. The rule gives you the possibility to define exactly the called method where the exception should be thrown in your test code. Moreover, you can easily match a string against the error message of the exception. In your case the code looks like this:

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

    @Test
    public void test() {
        //working code here...
        expectedException.expect(InvalidIPAddressException.class);
        IPAddress addr = new IPAddress(null);
    }

UPDATE: In his book Practical Unit Testing with JUnit and Mockito Tomek Kaczanowski argues against the use of ExpectedException, because the rule "breaks the arrange/act/assert [...] flow" of a Unit test (he suggests to use Catch Exception Library instead). Although I can understand his argument, I think using the rule is fine if you do not want to introduce another 3rd-party library (using the rule is better than catching the exception "manually" anyway).


For the null test you can simply do this:

public void testNullParameter() {
    try {
            IPAddress addr = new IPAddress(null);
            fail("InvalidIPAddressException not thrown.");
    }
    catch(InvalidIPAddressException e) { }
}

If the exception has a message, you could also check that message in the catch if you wish. E.g.

String actual = e.getMessage();
assertEquals("Null is not a valid IP Address", actual);

For the valid test you don't need to catch the exception. A test will automatically fail if an exception is thrown and not caught. So way #1 would be all you need as it will fail and the stack trace will be available to you anyway for your viewing pleasure.


if i understand your question, the answer is either - personal preference.

personally i throw my exceptions in tests. in my opinion a test failing by assertion is equivalent to a test failing by an uncaught exception. both show something that needs to be fixed.

the important thing to remember in testing is code coverage.


In general way #1 is the way to go, there is no reason to call out a failure over an error - either way the test essentially failed.

The only time way #2 makes sense if you need a good message of what went wrong, and just an exception won't give that to you. Then catching and failing can make sense to better announce the reason of the failure.


Reg: Testing for Exceptions
I agree with "Pascal Thivent", ie use @Test (expected=InvalidIPAddressException.class)


Reg: Testing for testValidIPAddress

IPAddress addr = new IPAddress("127.0.0.1");
byte[] octets = addr.getOctets();

I would write a test like

class IPAddressTests
{

    [Test]
    public void getOctets_ForAValidIPAddress_ShouldReturnCorrectOctect()
    {
         // Test code here
    }

}

The point is when testinput is VALID ipAddress
The test must be on the public methods/capabilities on the class asserting that they are working as excepted


IMO it is better to handle the exception and show appropriate messaging from the test than throwing it from a test.

ReferenceURL : https://stackoverflow.com/questions/1836364/bad-form-for-junit-test-to-throw-exception

반응형