CHAPTER9 - 리팩터링, 테스팅 (2)

2025. 3. 25. 23:41Book/모던 자바 인 액션

목차.

1. 람다 표현식 테스팅

 

3. 람다 테스팅

 

 

일반적으로 좋은 프로그래머라면 프로그램이 의도적으로 동작하는지 단위 테스팅을 진행한다. 소스 코드의 일부가 예상된 결과를 도출할 것이라 단언하는 테스트 케이스를 구현하는 것이다.

public record Point(int x, int y) {

    public Point moveRight(int distance) {
        return new Point(x + distance, y);
    }
}

@Test
void testMoveRightBy() {
    Point p1 = new Point(5, 5);
    Point p2 = p1.moveRight(10);
    assertEquals(15, p2.x());
    assertEquals(5, p2.y());
}

 

 

 

3.1 보이는 람다 표현식의 동작 테스팅

 

moveRightBy는 public이므로 위 코드는 문제없이 작동한다. 하지만 람다는 익명이므로 테스트 코드 이름을 호출할 수 없다.

 

따라서 필요하다면 람다를 필드에 저장해서 재사용할 수 있으며 람다의 로직을 테스트 할 수 있다. 예를 들어 Point 클래스에 compareByXandThenY라는 정적 필드를 추가했다고 가정하자

public record Point(int x, int y) {

    public Point moveRight(int distance) {
        return new Point(x + distance, y);
    }

    public final static Comparator<Point> compareByXAndThenY =
        comparing(Point::x).thenComparing(Point::y);
}

 

람다 표현식은 함수형 인터페이스의 인스턴스를 생성한다. 따라서 생성된 인스턴스의 동작으로 람다 표현식을 테스트할 수 있다. 다음은 Comparator 객체 compareByXAndThenY에 다양한 인수로 compare 메서드를 호출하면서 예상대로 동작하는지 테스트하는 코드다.

@Test
void testComparingTwoPoints() {
    Point p1 = new Point(10, 15);
    Point p2 = new Point(10, 20);

    int result = Point.compareByXAndThenY.compare(p1, p2);
    assertTrue(result < 0);
}

 

 

 

3.2 람다를 사용하는 메서드의 동작에 집중하라

 

람다의 목표는 정해진 동작을 다른 메서드에서 사용할 수 있도록 하나의 조각으로 캡슐화하는 것이다. 그러려면 세부 구현을 포함하는 람다 표현식을 공개하지 말아야 한다. 람다 표현식을 사용하는 메서드의 동작을 테스트함으로써 람다를 공개하지 않으면서도 람다 표현식을 검증할 수 있다. 예를 들면 다음 moveAllPointsRightBy 메서드를 살펴보자

public record Point(int x, int y) {

    public Point moveRight(int distance) {
        return new Point(x + distance, y);
    }

    public final static Comparator<Point> compareByXAndThenY =
        comparing(Point::x).thenComparing(Point::y);

    public static List<Point> moveAllPointsRightBy(List<Point> points, int distance) {
        return points.stream()
                .map(point -> point.moveRight(distance))
                .toList();
    }
}

 

 

위 코드에 람다 표현식 point -> point.moveRight() 를 테스트하는 부분은 없다. 그냥 moveAllPointRightBy 메서드를 구현한 코드일 뿐이다. 

 

@Test
void testMoveAllPointsRightBy() {
    List<Point> points =
            List.of(new Point(5, 5), new Point(10, 5));

    List<Point> expectedPoints =
            List.of(new Point(15, 5), new Point(20, 5));

    List<Point> actualPoints = Point.moveAllPointsRightBy(points, 10);

    assertEquals(expectedPoints, actualPoints);
}

 

 

 

 

3.3 고차원 함수 테스팅

 

함수를 인수로 받거나 다른 함수를 반환하는 메서드(고차원 함수)는 좀 더 사용하기 어렵다. 메서드가 람다를 인수로 받는다면 다른 람다로 메서드의 동작을 테스트할 수 있다. 예를 들면 Predicate로 만든 filter 메서드이다.

public static List<Integer> filter(List<Integer> tList, Predicate<Integer> predicate) {
    return tList.stream()
            .filter(predicate)
            .toList();
}

@Test
void testFilter() {
    List<Integer> numbers = List.of(1, 2, 3, 4);
    List<Integer> even = filter(numbers, n -> n % 2 == 0);
    List<Integer> smallerThanThree = filter(numbers, n -> n < 3);
    assertEquals(List.of(2, 4), even);
    assertEquals(List.of(1, 2), smallerThanThree);
}

 

테스트해야 할 메서드가 다른 함수를 반환한다면 Comparator에서 살펴봤던 것처럼 함수형 인터페이스의 인스턴스로 간주하고 함수의 동작을 테스트할 수 있다.