포스트

2023-07-19-TIL

2023-07-19-TIL

Today I Learned

JUnit5 테스트 예제 저장소

JUnit 5 테스트를 작성할 때 참고할 예제 저장소들이다. 기본 기능을 보여 주는 예제(@DisplayName, @Nested, @ParameterizedTest, @ExtendWith 등)와 함께, JUnit 5 위에서 Cucumber로 BDD 시나리오를 실행하는 예제, Spring Boot와 묶은 예제, 테스트 결과를 Allure 리포트로 시각화하는 예제가 있다. JUnit 5는 테스트를 작성하는 API인 Jupiter와 테스트를 발견하고 실행하는 Platform이 분리되어 있어서, Cucumber 같은 다른 엔진도 같은 Platform 위에서 돌아간다는 점이 이런 조합을 가능하게 한다.

  • https://github.com/mkyong/junit5-examples
  • https://github.com/cronn/cucumber-junit5-example
  • https://github.com/rmondejar/spring-boot-junit5-example/tree/master
  • https://github.com/swtestacademy/allure-junit5-example/tree/master
  • https://www.swtestacademy.com/allure-report-junit/
  • https://github.com/bonigarcia/mastering-junit5/tree/master
  • https://github.com/howtoprogram/junit5-examples/tree/master

@Spy vs @Mock

Mockito의 @Mock은 클래스의 껍데기만 흉내 낸 객체를 만든다. 모든 메서드는 아무것도 하지 않고 기본값(null, 0, 빈 컬렉션)을 돌려주며, 필요한 동작만 when(...).thenReturn(...)으로 정한다. @Spy는 실제 객체를 감싼다. Mockito Javadoc 설명대로 spy는 스텁하지 않은 메서드에 대해 실제 메서드를 호출하고, 일부 메서드만 바꾸는 부분 모킹(partial mocking)에 쓰인다.

Javadoc은 spy를 레거시 코드를 다룰 때처럼 신중하게, 가끔만 쓰라고 권한다. 실제 메서드가 호출된다는 점 때문에 생기는 함정도 짚는다. when(spy.get(0))은 스텁을 정의하는 순간 실제 get(0)이 실행되어 예외가 날 수 있으므로, spy를 스텁할 때는 doReturn("foo").when(spy).get(0) 형태를 쓴다.

  • https://javapointers.com/java/unit-test/difference-between-spy-and-mock-in-mockito/
  • https://stackoverflow.com/questions/28295625/mockito-spy-vs-mock
  • https://luizcostatech.medium.com/what-is-the-difference-between-mock-and-spy-c5585922d9fd
  • https://www.javatpoint.com/mock-vs-stub-vs-spy
  • https://www.baeldung.com/mockito-spy
  • Mockito Javadoc

RestAssured

REST Assured는 HTTP API를 검증하는 Java DSL이다. 공식 사이트는 Java에서 REST 서비스를 테스트하는 일이 Ruby나 Groovy 같은 동적 언어보다 번거롭다는 문제의식에서 출발했다고 설명한다. given()에 요청 조건을, when()에 호출을, then()에 응답 검증을 쓰는 구조라 테스트가 시나리오처럼 읽힌다. Spring Boot에서는 @SpringBootTest(webEnvironment = RANDOM_PORT)로 실제 서버를 띄우고 포트를 지정해 인수 테스트에 쓰는 경우가 많다. MockMvc가 서블릿 컨테이너 없이 Spring MVC를 호출하는 것과 달리, 실제 HTTP 요청을 보내므로 필터와 직렬화까지 포함해 검증한다.

1
2
3
4
5
6
7
8
given()
    .contentType(ContentType.JSON)
    .body(request)
.when()
    .post("/members")
.then()
    .statusCode(201)
    .body("name", equalTo("kim"));
  • https://rest-assured.io/
  • https://github.com/rest-assured/rest-assured/wiki/Usage
  • https://velog.io/@jjy5349/RestAssured%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0

Spring Boot와 JUnit5

애플리케이션을 띄우지 않고 단위 테스트를 쓰는 방법과 Spring Boot 테스트의 함정을 다룬 자료들이다. 서비스 로직은 @ExtendWith(MockitoExtension.class)와 @Mock, @InjectMocks만으로 스프링 컨텍스트 없이 테스트할 수 있어 빠르다. 컨텍스트가 필요하면 @WebMvcTest, @DataJpaTest 같은 슬라이스 테스트로 범위를 줄이고, @SpringBootTest는 통합 테스트에 쓴다. Spring 테스트 프레임워크는 설정이 같은 테스트끼리 애플리케이션 컨텍스트를 캐시하므로, 테스트 클래스마다 @MockBean 조합이나 프로퍼티를 다르게 주면 컨텍스트를 매번 새로 띄워 테스트가 느려진다는 점이 대표적인 함정이다.

  • https://github.com/rmondejar/spring-boot-junit5-example/blob/master/src/test/java/example/service/BookServiceTest.java
  • https://stackoverflow.com/questions/64780100/write-unit-test-in-springboot-without-start-application
  • https://www.baeldung.com/spring-boot-testing-pitfalls

Mocking vs Stubbing

테스트에서 실제 객체 대신 쓰는 대역을 통틀어 테스트 더블이라 하고, Dummy, Fake, Stub, Spy, Mock으로 나눈다. Martin Fowler의 “Mocks Aren’t Stubs”에 따르면 Stub은 테스트 중 호출에 미리 준비한 답을 돌려주는 객체이고, Spy는 거기에 더해 어떻게 호출되었는지를 기록하는 Stub이며, Mock은 받을 호출에 대한 기대를 미리 프로그래밍해 둔 객체다. Fowler가 강조하는 차이는 검증 방식이다. Stub을 쓰는 테스트는 실행 후 대상과 협력 객체의 상태를 확인하는 상태 검증(state verification)을 하고, Mock을 쓰는 테스트는 대상이 협력 객체를 올바르게 호출했는지 확인하는 행위 검증(behavior verification)을 한다. Mockito의 when().thenReturn()은 스텁, verify()는 행위 검증에 해당한다.

이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다