Jam*_*wiy 5 testing lambda mocking java-8
我想知道是否有人找到一种方法来存储/模拟lambda中的逻辑而不会使lambda的可见性?
public List<Item> processFile(String fileName) {
// do some magic..
Function<String, List<String>> reader = (fileName) -> {
List<String> items = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
String output;
while ((output = br.readLine()) != null) {
items.add(output);
}
} catch (IOException e) {
e.printStackTrace();
}
return items;
};
List<String> lines = reader.apply("file.csv");
// do some more magic..
}
Run Code Online (Sandbox Code Playgroud)
我会说规则是如果一个lambda表达式如此复杂以至于你觉得需要模拟它的一些部分,那它可能太复杂了.它应该被分解成一起组成的小块,或者可能需要调整模型以使其更适合于组合.
我会说Andrey Chaschev的答案表明参数化一个依赖是一个很好的,可能适用于某些情况.所以,+1为此.一个可以继续这一进程,并分解加工成更小的碎片,就像这样:
public List<Item> processFile(
String fileName,
Function<String, BufferedReader> toReader,
Function<BufferedReader, List<String>> toStringList,
Function<List<String>, List<Item>> toItemList)
{
List<String> lines = null;
try (BufferedReader br = toReader.apply(fileName)) {
lines = toStringList.apply(br);
} catch (IOException ioe) { /* ... */ }
return toItemList.apply(lines);
}
Run Code Online (Sandbox Code Playgroud)
不过,有几点意见.首先,这不起作用,因为各种lambdas抛出讨厌的东西IOExceptions
,它们被检查,并且Function
没有声明类型抛出该异常.第二个是你必须传递给这个函数的lambdas是怪异的.即使这不起作用(因为检查异常)我写出来了:
void processAnActualFile() {
List<Item> items = processFile(
"file.csv",
fname -> new BufferedReader(new FileReader(fname)),
// ERROR: uncaught IOException
br -> {
List<String> result = new ArrayList<>();
String line;
while ((line = br.readLine()) != null) {
result.add(line);
}
return result;
}, // ERROR: uncaught IOException
stringList -> {
List<Item> result = new ArrayList<>();
for (String line : stringList) {
result.add(new Item(line));
}
return result;
});
}
Run Code Online (Sandbox Code Playgroud)
啊! 我想我发现了新的代码味道:
如果你必须在lambda中写一个for循环或while循环,你就会做错事.
这里发生了一些事情.首先,I/O库实际上是由不同件实现(的InputStream
,Reader
,BufferedReader
),它们紧耦合.尝试将它们分开是没有用的.实际上,该库已经发展,因此有一些便利工具(例如NIO Files.readAllLines
)可以为您处理大量的腿部工作.
更重要的一点是,设计在它们之间传递值的聚合(列表)并组成这些函数的函数,实际上是错误的方法.它导致每个函数必须在其中编写循环.我们真正想做的是编写每个操作单个值的函数,然后让Java 8中的新Streams库为我们处理聚合.
从评论所描述的代码中提取的关键功能"做一些更多的魔法"转换List<String>
成List<Item>
.我们想要提取将一个 转换String
为一个的计算Item
,如下所示:
class Item {
static Item fromString(String s) {
// do a little bit of magic
}
}
Run Code Online (Sandbox Code Playgroud)
一旦你有了这个,那么你可以让Streams和NIO库为你做一堆工作:
public List<Item> processFile(String fileName) {
try (Stream<String> lines = Files.lines(Paths.get(fileName))) {
return lines.map(Item::fromString)
.collect(Collectors.toList());
} catch (IOException ioe) {
ioe.printStackTrace();
return Collections.emptyList();
}
}
Run Code Online (Sandbox Code Playgroud)
(注意,这个简短方法的一半用于处理IOException
.)
现在,如果你想进行一些单元测试,那么你真正需要测试的是一点魔力.所以你将它包装到不同的流管道中,如下所示:
void testItemCreation() {
List<Item> result =
Arrays.asList("first", "second", "third")
.stream()
.map(Item::fromString)
.collect(Collectors.toList());
// make assertions over result
}
Run Code Online (Sandbox Code Playgroud)
(实际上,即使这样也不是很正确.你想要编写将单行转换为单行的单元测试Item
.但也许你在某处有一些测试数据,所以你可以用这种方式将它转换成项目列表,然后对列表中结果项的关系进行全局断言.)
我离你最初的如何分裂lambda的问题走了很远.请原谅我放纵自己.
原始示例中的lambda非常不幸,因为Java I/O库非常繁琐,并且NIO库中有新的API可以将示例转换为单行.
尽管如此,这里的教训是,不是组合处理聚合的函数,而是组合处理单个值的函数,并让流处理聚合.这样,您可以通过以不同方式将流管道插入在一起来进行测试,而不是通过模拟复杂lambda的位来进行测试.
归档时间: |
|
查看次数: |
3823 次 |
最近记录: |