티스토리 뷰

개발

자바 테스트 JUnit5 아니면 spock

달리는개발자 2018. 8. 22. 15:34

자바프로그램 테스트를 위해서 주로 JUnit을 사용합니다. Spockframework도 있는데 각각의 장단점이 있는 것 같습니다.


JUnit5와 Spockframework에 관해서 정리합니다.


JUnit5란?

3개의 하위 모듈의 서브 프로젝트로 구성됨


JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage


JUnit Platform : 테스트 엔진, Console Launcher, JUni4 기반 러너 등 테스트 프레임워크를 시작하기 위한 기초적인 것을 제공한다.

JUnit Jupiter : JUnit5를 위한 테스트 엔진 및 API를 제공한다.

JUnit Vintage : JUnit3, 4를 위한 테스트 엔진을 제공한다. 하위 호환성을 위해 존재하는 프로젝트.



Migrating from JUnit4

  • Annotations reside in the org.junit.jupiter.api package. (JUni5 기본 패키지, 하위 버전과 패키지가 다름)

  • Assertions reside in org.junit.jupiter.api.Assertions

  • Assumptions reside in org.junit.jupiter.api.Assumptions.

  • @Before and @After no longer exist; use @BeforeEach and @AfterEach instead.

  • @BeforeClass and @AfterClass no longer exist; use @BeforeAll and @AfterAll instead.

  • @Ignore no longer exists: use @Disabled instead.

  • @Category no longer exists; use @Tag instead.

  • @RunWith no longer exists; superseded by @ExtendWith.

  • @Rule and @ClassRule no longer exist; superseded by @ExtendWith; see the following section for partial rule support.

Features

JUnit 5

JUnit 4

Declares a test method@Test@Test
Denotes that the annotated method will be executed before all test methods  in the current class@BeforeAll@BeforeClass
Denotes that the annotated method will be executed after all test methods  in the current class@AfterAll@AfterClass
Denotes that the annotated method will be executed before each test method@BeforeEach@Before
Denotes that the annotated method will be executed after each test method@AfterEach@After
Disable a test method or a test class@Disable@Ignore
Denotes a method is a test factory for dynamic tests in JUnit 5@TestFactoryN/A
Denotes that the annotated class is a nested, non-static test class@NestedN/A
Declare tags for filtering tests@Tag@Category
Register custom extensions in JUnit 5@ExtendWithN/A
Repeated Tests in JUnit 5@RepeatedTestN/A




@DisplayName을 사용하면 띄어쓰기, 특수문자, 이모지도 허용되어서 좀 더 알기 쉽게 테스트 제목을 작성할 수 있습니다.


class JUnit5Test {

@DisplayName("해당 글자가 컬렉션에 포함되어 있는지 확인 👏")
@Test
void testOwners() {
List<String> owners = Arrays.asList("예파파", "예마마");

assertAll(
() -> assertTrue(owners.contains("예파파"), "예파파 포함됨"),
() -> assertTrue(owners.contains("예마마1"), "예마마 포함됨")
);
}
}


JUnit4에서 @Test(expected=..... 를 사용하여 Exception을 확인하였지만 message까지 확인하려면 @Rule 애노테이션을 달은 ExpectedException을 만들고 테스트 내에서 expect, expectMessage 메서드를 통해 확인해야 했습니다.


JUnit4에서는 아래와 같이 처리합니다.


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


@Tag를 통해서 필터링 후에 테스트를 실행할 수 있습니다.

@Tag("integration"), @Tag("slow") 등등 프로젝트에 맞는 테스트를 만들고 사용자 정의 애노테이션으로 만들어서 사용하기도 합니다.

예를 들면 @Tag("integration")을 사용해서 @IntegrationTest, @CITest 란 애노테이션을 정의합니다.


@TestFactory를 통해서 동적 테스트를 만들 수 있고요


다음과 같이 @Nested 애노테이션을 통해서 테스트 그룹을 더 잘 표현할 수 있게 만들 수 있습니다.


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

import java.util.EmptyStackException;
import java.util.Stack;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

@DisplayName("A stack")
class TestingAStackDemo {

    Stack<Object> stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, () -> stack.pop());
        }

        @Test
        @DisplayName("throws EmptyStackException when peeked")
        void throwsExceptionWhenPeeked() {
            assertThrows(EmptyStackException.class, () -> stack.peek());
        }

        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }

            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() {
                assertEquals(anElement, stack.pop());
                assertTrue(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when peeked but remains not empty")
            void returnElementWhenPeeked() {
                assertEquals(anElement, stack.peek());
                assertFalse(stack.isEmpty());
            }
        }
    }
}




junit nested test







영상 슬라이드 보기



Spock Testing and Specification Framework

쉬운 Stubbing


subscriber.receive("message1") >> "ok"
subscriber.receive("message2") >> "fail"





Data Driven Testing


동일한 테스트를 다양한 입력과 예상 결과값으로 테스트 하는 것이 좋을 때가 있는데 Spock이 이 부분에 장점이 있습니다.


아래와 같은 테스트가 있을 때


class MathSpec extends Specification {
  def "maximum of two numbers"() {
    expect:
    // exercise math method for a few different inputs
    Math.max(1, 3) == 3
    Math.max(7, 4) == 7
    Math.max(0, 0) == 0
  }
}



Data table을 이용해서 처리합니다.

class MathSpec extends Specification {
  def "maximum of two numbers"(int a, int b, int c) {
    expect:
    Math.max(a, b) == c

    where:
    a | b | c
    1 | 3 | 3
    7 | 4 | 7
    0 | 0 | 0
  }
}



Intellij 2018.2.2에서 method signiture를 변경했으나 인지를 못하네요

당연히 테스트를 돌려보면 알 수 있는 부분이긴 하지만 아쉽네요




참고

JUnit 5 가이드

https://junit.org/junit5/docs/current/user-guide


JUnit4, JUnit 5 비교

http://www.vogella.com/tutorials/JUnit/article.html


Spock Testing and Specification Framework

http://spockframework.org/spock/docs/1.1/all_in_one.html


https://www.baeldung.com/junit-assert-exception

https://howtodoinjava.com/junit5/expected-exception-example/

https://howtoprogram.xyz/2016/08/10/junit-5-vs-junit-4/

https://www.baeldung.com/junit-before-beforeclass-beforeeach-beforeall

https://blog.codefx.org/libraries/junit-5-basics/

https://blog.codefx.org/libraries/junit-5-setup/

http://blog.codeleak.pl/2017/11/junit-5-meets-assertj.html



intellij 2018.1 @Tag annotaion support

https://jaxenter.com/intellij-ide-public-preview-build-142006.html

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함