[JUnit] Collection 안에 있는 property 테스트 - hasProperty, containsInAnyOrder

by 스뎅(thDeng) on

JUnit으로 객체가 property(field)로 특정값들을 가지고 있는지 체크할 때는 hasProperty를 사용하면 된다.

// "bar" 라는 property가 있는지 체크
assertThat(new Foo(10), hasProperty("bar"));

// "bar" 라는 property가 있고, 그 값이 10인지 체크
assertThat(new Foo(10), hasProperty("bar", is(10)));


@Getter
@AllArgsConstructor
public class Foo {
    private int bar;
}

그리고 이 테스트를 Collection에 있는 객체들을 대상으로 체크하고 싶다면 containsInAnyOrder와 함께 사용하면 된다.

    Set<Foo> actual = ImmutableSet.of(new Foo(10), new Foo(20), new Foo(30));

    assertThat(actual, containsInAnyOrder(
        hasProperty("bar", is(10)),
        hasProperty("bar", is(20)),
        hasProperty("bar", is(30))
    ));

public class 이느뇨??

하지만!! (두둥!!) 마음대로 되면 재미가 없는지.. 막상 테스트를 만들고 돌리니 아래처럼 오류 메시지만 나오고 원하는대로 제대로 테스트가 돌지 않는다.

java.lang.AssertionError:
Expected: iterable over [hasProperty("bar", is <10>), hasProperty("bar", is <20>), hasProperty("bar", is <30>)] in any order
     but: Not matched: <kr.leocat.test.PropertyTest$Foo@574caa3f>

    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
    at org.junit.Assert.assertThat(Assert.java:956)
    at org.junit.Assert.assertThat(Assert.java:923)
    at kr.leocat.test.PropertyTest.test(PropertyTest.java:18)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

테스트코드는 아래와 같다. containsInAnyOrder와 함께 사용한 것 말고는 특별히 이상한 것은 없다.

package kr.leocat.test;

import com.google.common.collect.ImmutableSet;
import lombok.Getter;
import org.junit.Test;

import java.util.Set;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;

public class PropertyTest {

    @Test
    public void test() {
        Set<Foo> actual = ImmutableSet.of(new Foo(10), new Foo(20), new Foo(30));

        assertThat(actual, containsInAnyOrder(
            hasProperty("bar", is(10)),
            hasProperty("bar", is(20)),
            hasProperty("bar", is(30))
        ));
    }

    // 결론부터 말하면, 이 테스트 클래스가 public 이야아 한다.
    @Getter
    private class Foo {
        private int bar;

        public Foo(int bar) {
            this.bar = bar;
        }
    }

}

그런데 왜 안 될까?? containsInAnyOrderhasItem(allOf())로 바꿔서 테스트해 봐도 동일한 오류가 발생한다.

결국 containsInAnyOrder를 제거하고 테스트하니 아래처럼 오류 메시지가 보였다. 테스트만을 위해 Foo 클래스님을 private inner class로 만들었더니 접근이 안 된단다. (아.. 토나와. 처음부터 쫌만 더 친절히 알려주지!! T_T)

java.lang.AssertionError:
Expected: hasProperty("bar", is <10>)
     but: Class org.hamcrest.beans.HasPropertyWithValue$1 can not access a member of class kr.leocat.test.PropertyTest$Foo with modifiers "public"

결론

객체가 property를 가졌는지 또 특정 값을 가졌는지 테스트는 hasProperty를 통하면 된다. collection에 담긴 객체는 containsInAnyOrderhasProperty를 함께 사용하면 된다.

별도로 명시하지 않을 경우, 이 블로그의 포스트는 다음 라이선스에 따라 사용할 수 있습니다: Creative Commons License CC Attribution-NonCommercial-ShareAlike 4.0 International License