# 测试和调试

## 测试用例

public class Point {
private final int x;
private final int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

public int getX() {
return x;
}

public int getY() {
return y;
}

public Point moveRightBy(int x) {
return new Point(this.x + x, this.y);
}
}


public class PointTest {

@Test
public void testMoveRightBy() {
Point p1 = new Point(5, 5);
Point p2 = p1.moveRightBy(10);

Assert.assertEquals(15, p2.getX());
Assert.assertEquals(5, p2.getY());
}
}


## 测试可见 Lambda 函数的行为

public class Point {
public final static Comparator<Point> COMPARE_BY_X_AND_THEN_Y =
comparing(Point::getX).thenComparing(Point::getY);
...
}


@Test
public void testComparingTwoPoints() {
Point p1 = new Point(10, 15);
Point p2 = new Point(10, 20);
int result = Point.COMPARE_BY_X_AND_THEN_Y.compare(p1 , p2);
Assert.assertEquals(-1, result);
}


ps: 实际使用 lambda 表达式时，不会将其定义为这种变量。因此，这只是一种测试的方式。个人建议是直接测试完整的方法是否符合预期即可。

## 测试使用 Lambda 的方法的行为

public static List<Point> moveAllPointsRightBy(List<Point> points, int x) {
return points.stream()
.map(p -> new Point(p.getX() + x, p.getY()))
.collect(toList());
}


@Test
public void testMoveAllPointsRightBy() {
List<Point> points =
Arrays.asList(new Point(5, 5), new Point(10, 5));
List<Point> expectedPoints =
Arrays.asList(new Point(15, 5), new Point(20, 5));
List<Point> newPoints = Point.moveAllPointsRightBy(points, 10);
Assert.assertEquals(expectedPoints, newPoints);
}


# 调试

• 查看栈跟踪

• 输出日志

## 查看栈跟踪

### Lambda表达式和栈跟踪

public class Debugging {
public static void main(String[] args) {
List<Point> points = Arrays.asList(new Point(12, 2), null);
points.stream().map(p -> p.getX()).forEach(System.out::println);
}
}


12
// 这行中的 $0 是什么意思？ at xin.codedream.java8.chap8.Debugging.lambda$main$0(Debugging.java:15) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
...


### lambda 表达式没有名称的问题

at xin.codedream.java8.chap8.Debugging.lambda$main$0(Debugging.java:15)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)


### lambda 方法引用没有名称的问题

at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)


### lambda 方法引用可以显示的场景

public class Debugging {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.stream().map(Debugging::divideByZero).forEach(System
.out::println);
}

public static int divideByZero(int n) {
return n / 0;
}
}


Exception in thread "main" java.lang.ArithmeticException: / by zero
// divideByZero正确地输出到栈跟踪中
at xin.codedream.java8.chap8.Debugging.divideByZero(Debugging.java:20)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
...


ps: 这会给程序的测试带来一定的成本。需要注意，尽可能的使用方法引用，让堆栈信息打印出方法名称。

## 使用日志调试

List<Integer> numbers = Arrays.asList(2, 3, 4, 5);
numbers.stream()
.map(x -> x + 17)
.filter(x -> x % 2 == 0)
.limit(3)
.forEach(System.out::println);


20
22


### peek

peek 的设计初衷就是在流的每个元素恢复运行之前，插入执行一个动作。

List<Integer> result = Stream.of(2, 3, 4, 5)
.peek(x -> System.out.println("taking from stream: " + x)).map(x -> x + 17)
.peek(x -> System.out.println("after map: " + x)).filter(x -> x % 2 == 0)
.peek(x -> System.out.println("after filter: " + x)).limit(3)
.peek(x -> System.out.println("after limit: " + x)).collect(toList());


taking from stream: 2
after map: 19
taking from stream: 3
after map: 20
after filter: 20
after limit: 20
taking from stream: 4
after map: 21
taking from stream: 5
after map: 22
after filter: 22
after limit: 22


# 个人收获

1. 正确比优美更重要。客户可不管你的程序写的优美与否，保证正确是最基本的。

2. 解决一个问题的方式是多样的，知道其发生的原因，可以帮助我们更好的解决问题。

《java8 实战》

【Java8实战】开始使用流

JDK8 实战系列