JUnit4에서의 예외 테스트를 편하게 친다!

2018/6/13 추가
jUtaime은 JUnit5의 등장으로 그 사명을 마쳤습니다.
오랫동안 사랑 해 주셔서 감사합니다 m (_ _) m
여러분 JUnit5를 사용합시다.

What's an issue ?



JUnit에서의 예외 검증 코드를 더 깔끔하게 쓰고 싶다!
그렇게 생각하고 있는 것은 분명 나만이 아닐 것...

before



예를 들면 Person#setAge(int) 라고 하는 자주 있는 메소드를 만들었다고 해서, 다음의 4점을 검증하고 싶다.
  • setAge(-1) 는 NG → IllegalArgumentException
  • setAge(0) 는 OK
  • setAge(200) OK : 초 장수 사회의 도착에 대비.
  • setAge(201) 는 NG → IllegalArgumentException 를 슬로우, 예외 메세지 있어

  • 통상의 JUnit4에서는 이런 느낌이 된다고 생각합니다.

    JUnit4의 일반적인 예외 테스트
    public class PersonTest {
    
        private Person person = new Person("John");
    
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test(expected = IllegalArgumentException.class)
        public void testSetAge1() {
            person.setAge(-1);
        }
    
        @Test
        public void testSetAge2() {
            try  {
                person.setAge(0);
                person.setAge(200);  // 200歳まではOKとする。
            } catch(RuntimeException e) {
                fail();
            }
        }
    
        @Test
        public void testSetAge3() {
            thrown.expect(IllegalArgumentException.class);
            thrown.expectMessage("Is he a zombie!?");
    
            person.setAge(201);
        }
    }
    

    어째서 단지 이런 곳의 검증으로, 이렇게 엉뚱한 코드를 쓰지 않으면 안 되나요! 흠

    after



    이것으로 좋다 ...

    이렇게 쓰고 싶다.
    public class PersonTest {
    
        @Test
        public void testSetAge() {
            Person p = new Person("John");
            assertThat(of(() -> p.setAge(-1)),  raise(IllegalArgumentException.class));
            assertThat(of(() -> p.setAge(0)),   raiseNothing());
            assertThat(of(() -> p.setAge(200)), raiseNothing());
            assertThat(of(() -> p.setAge(201)), raise(IllegalArgumentException.class, "Is he a zombie!?"));
        }
    }
    

    so that



    그래서, after를 실현하는 라이브러리를 만들었습니다.
    nmby/jetaime · GitHub
    nmby/jUtaime · GitHub (2015/7/26 이동했습니다.)

    usage


  • 예외를 슬로우 할 수 있는 검증 대상의 처리를, Testee.of() 안에 기술합니다.
  • 예외의 예외의 형태나 메세지, 원인(cause)의 형태등을, RaiseMatchers.raise() 등으로 지정합니다.
  • assertThat(Testee.of(SomeClass::someMethodShouldFail),
            RaiseMatchers.raise(SomeException.class, "expected message"));
    
    TesteeRaiseMatchers 는 static 가져오면 좋을 것입니다.
    다음과 같은 다양한 쓰기가 가능합니다.
    assertThat(of(() -> Integer.valueOf("abc")), raiseExact(NumberFormatException.class));
    assertThat(of(() -> Integer.valueOf("123")), raiseNothing());
    assertThat(of(() -> { Object o = null; o.toString(); }), raise(RuntimeException.class));
    assertThat(of(obj::dbOperation), rootCause(IOException.class));
    

    또한 다른 Matcher와 함께 사용할 수 있습니다.
    다음 예제에서는 mc st. 오 rg 에서 제공하는 anyOf , allOf , not 와 함께 사용합니다.
    // NullPointerException または IllegalArgumentException がスローされることを検証
    assertThat(of(() -> obj.someOperation(null)),
            anyOf(raise(NullPointerException.class), raise(IllegalArgumentException.class)));
    
    // NullPointerException 以外の何らかの実行時例外を原因として上位例外がスローされることを検証
    assertThat(of(() -> obj.someOperation(param)),
            allOf(raise(WrappingException.class, "expected message"),
                    rootCause(RuntimeException.class),
                    not(rootCause(NullPointerException.class))));
    
    allOf() 대신, 다음의 연결 스타일로 기술하는 것도 가능합니다.
    // NullPointerException 以外の何らかの実行時例外を原因として上位例外がスローされることを検証
    assertThat(of(() -> obj.someOperation(param)),
            raise(WrappingException.class, "expected message")
                    .rootCause(RuntimeException.class)
                    .not(rootCause(NullPointerException.class)));
    

    자세한 것은 javadoc 를 봐 ♪

    또한 eclipse에서 사용하면 제대로 보고서도 표시됩니다.


    dependencies


  • java 8 이상이 필요합니다.

  • hamcrest-core 도 필요합니다만, JUnit4 를 사용하고 있다면 벌써 들어가 있을 것입니다.
  • 좋은 웹페이지 즐겨찾기