주관적인 내용이 들어가 있습니다.

TDD 방법

  • 정답인지는 모르겠지만 TDD에 익숙해지기 위해서 아래와 같은 방식으로 진행해야겠다.
  • 큰 로직을 작게 나누어 검사
  • 큰 로직
    • ex) 작은 로직1
      테스트 코드 for 작은 로직1 → 실제 코드 for 작은 로직1 → 리팩토링 → (반복)
    • ex) 작은 로직2
      테스트 코드 for 작은 로직2 → 실제 코드 for 작은 로직2 → 리팩토링 → (반복)

Private method도 테스트해야하는가?

  • Private method를 잘 테스트하는 것 만으로 해당 private method의 무결성을 보장할 수 있다.
  • 하지만 개발자가 불안하다고 느끼면 짜는것이 좋다
    • private 메소드를 public으로 잠시 바꾸어 테스트한다.
    • 애초에 과연 private일 필요가 있는가? 없다면 public으로 열어도 될것 같다
    • 리플렉션을 통해 테스트한다.
    • 해당 private 메소드를 호출하는 public 메소드의 파라미터의 모든 parameterized 테스트를 진행하여 모든 파라미터들을 무사히 통과한다면 해당 private 메소드를 테스트 한것으로 간주한다. 

Reflection API

  • 자바에서 기본적으로 지공하는 api
  • 구체적인 클래스 타입을 알지 못해도 클래스의 정보에 접근할 수 있게한다.
public class Car {
	public void move() { … }
}

// 컴파일 타임에 Object라는 타입이 결정됨
// Object 클래스의 인스턴스 변수와 메서드만  사용할 수 있다.
// 따라서 컴파일 에러 발생한다.
Object obj = new Car();
obj.move();


// Reflection API 사용
Object obj = new Car();
Class carClass = Car.class;
Method move. = carClass.getMethod(“move”);

// move 메서드 실행
// invoke(메서드를 실행시킬 객체, 메소드 파리미터)
move.invoke(obj, null);

자바에서는 JVM이 실행되면 사용자가 작성한 자바 코드가 컴파일러를 거쳐 바이트 코드로 변환되어 static 영역에 저장된다. Reflection API는 이 정보를 활용한다. 그래서 클래스 이름만 알고 있다면 언제든 static 영역을 뒤져서 정보를 가져올 수 있는 것이다.

  • 단점
    • 성능 오버헤드
    • 추상화 파괴(private 인스턴스 변수, 메서드에 접근)
  • 나는 왜 알아보는가?
    • private 메소드를 테스트할 방식을 찾던 중 알게되어 찾아보게 되었다.
    • 어떻게 사용하는가?
Method maxMethod = Calculator.class.getDeclaredMethod("max", int.class, int.class);
maxMethod.setAccessible(true);

int source = 10;
int target = 5;

int maxValue = (int)maxMethod.invoke(calculator, source, target);
  • getMethod(String name, Class<?>... parameterTypes)
    public 메소드, superclasses 와 superinterface 의 메소드 전부 가져올 수 있다.

  • getDeclaredMethod(String name, Class<?>... parameterTypes)
    해당 클래스에만 선언된 메소드만 가져올 수 있다.

Reflection API 참고

parameterized 테스트

  • Junit 5

  • @Parameterized

  • 하나의 테스트 메서드로 여러개의 파라미터에 대해서 테스트할 수 있다.

  • 최소 하나의 source 어노테이션을 붙여주어야 한다.

  • @ValueSource
    단일 배열을 지정할 수 있으며 매개 변수화 된 테스트 호출마다 단일 인수를 제공하는 데만 사용

@ParameterizedTest
@ValueSource(strings = {"q", "qwerasdfzxcv", "qq23"})
void createUserException(String name){
      IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> new User(VALID_EMAIL, name, password));
      assertThat(e.getMessage()).isEqualTo(NAME_NOT_MATCH_MESSAGE);
}
  • @NullSource, @EmptySource
    파라미터 값으로 null과 empty를 준다
@ParameterizedTest
@NullSource
@EmptySource
void nullEmptyStrings(String text) {
    assertTrue(text == null || text.trim().isEmpty());
}
  • @NullAndEmptySource
    Null 과 empty를 함께 제공, @ValueSrouce와 함께 사용 가능
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = { " ", "   ", "\t", "\n" })
void nullEmptyAndBlankStrings(String text) {
    assertTrue(text == null || text.trim().isEmpty());
}
  • @MethodSource
    복잡한 인수 제공
@ParameterizedTest
@MethodSource("invalidParameters")
void invalidCreate(String name, String email, String password, String message, String exceptionMessage) {
    IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> new User(email, name, password));
    assertThat(e.getMessage()).isEqualTo(exceptionMessage);
}

static Stream<Arguments> invalidParameters() throws Throwable {
	return Stream.of(
    	Arguments.of("a", VALID_EMAIL, VALID_PASSWORD, "name 2자 미만", NAME_NOT_MATCH_MESSAGE),
        Arguments.of("qwertasdfzpl", VALID_EMAIL, VALID_PASSWORD, "name 10자 초과", NAME_NOT_MATCH_MESSAGE));
}

parameterized 테스트 참고

profile
꾸준히 나아가자 🐢

0개의 댓글

Powered by GraphCDN, the GraphQL CDN