我想实现并单元测试使用Java 8 Stream API(Stream接口和Collectors类)聚合某些数据的方法。请参阅以下代码:
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class AggregationKata {
public static Map<String, Double> getAverageGradeByDepartment(Stream<Student> students) {
return students
.collect(Collectors.groupingBy(Student::getDepartment,
Collectors.averagingDouble(Student::getGrade)));
}
}
我想编写一个单元测试,在实现此方法时强制使用Stream API。换句话说,必须使用Stream接口和Collectors类。我想我需要在单元测试中使用java反射,但我无法弄清楚如何。
问题是 - 我该如何编写这样的单元测试?谢谢!
答案 0 :(得分:1)
您想要做的不是单元测试
你不想测试你班级的行为
您希望确保实现遵循特定的编码规则。
要解决此要求,您应该在每次提交时在Jenkins中处理它的工具(例如Sonar)或甚至是静态代码分析工具中定义规则。
通过在Sonar中实现自定义规则,您可以使用反射和检查源代码,但不能直接使用(API为您完成工作)。
例如:
@Override
public void visitNode(Tree tree) {
MethodTree method = (MethodTree) tree;
if (method.parameters().size() == 1) {
MethodSymbol symbol = method.symbol();
reportIssue(method.simpleName(), "Never do that!");
}
}
答案 1 :(得分:0)
Reflection无法访问方法体。所以它不能用于强制执行特定的实现。
分析方法体的唯一方法是源代码分析(解析器+约束),或者如果代码已经编译了字节码分析。
所以使用java的解析器并实现你想要的必要约束;或使用asm等字节码工具。
在我看来,字节码分析比源代码更容易,所以我会使用asm。
这里有一个代表性的原始示例,代码将为您的示例打印[java/util/stream/Collectors::groupingBy, java/util/stream/Stream::collect, java/util/stream/Collectors::averagingDouble]
。 (需要org.ow2.asm:asm:jar:5.2)
public static class MethodsCalledClassVisitor extends ClassVisitor {
private Set<String> methodsCalled = new HashSet<>();
public MethodsCalledClassVisitor() {
super(Opcodes.ASM5);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if ("getAverageGradeByDepartment".equals(name)) {
return new MyMethodVisitor(this);
} else {
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
public void addMethodCalled(String methodCall) {
methodsCalled.add(methodCall);
}
public Set<String> getMethodsCalled() {
return methodsCalled;
}
}
public static class MyMethodVisitor extends MethodVisitor {
private final MethodsCalledClassVisitor visit;
public MyMethodVisitor(MethodsCalledClassVisitor visit) {
super(Opcodes.ASM5);
this.visit = visit;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
visit.addMethodCalled(owner + "::" + name);
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
public static void main(String[] args) throws IOException {
ClassReader reader = new ClassReader(AggregationKata.class.getName());
MethodsCalledClassVisitor classVisitor = new MethodsCalledClassVisitor();
reader.accept(classVisitor, 0);
System.out.println(classVisitor.getMethodsCalled());
}