使用 mockito 和多个 return 值模拟 CSV 阅读器
Mock CSVReader with mockito and multible return values
我不想嘲笑 CSVReader。所以我的模拟每次都应该 return 一个新数组,这应该是通用的。
最后一个值应该为空。
前
nextLine() -> ["a","b","c"]
nextLine() -> ["a","b","c"]
nextLine() -> null
我的模拟类:
import au.com.bytecode.opencsv.CSVReader;
import com.sun.javafx.beans.annotations.NonNull;
import org.mockito.Mockito;
import org.mockito.stubbing.OngoingStubbing;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class CSVReaderMock {
CSVReader reader;
private boolean linesCorrectInitialized;
public CSVReaderMock()
{
reader = mock(CSVReader.class);
}
public CSVReaderMock returnLines(@NonNull List<String> lines) {
// the last value has to be null
lines.add(null);
try {
for (String line : lines) {
String[] lineArr = null;
if (line != null) {
lineArr = line.split(",");
}
when(reader.readNext()).thenReturn(lineArr);
}
linesCorrectInitialized = true;
} catch (IOException e) {
e.printStackTrace();
};
return this;
}
public CSVReader create() {
if (!linesCorrectInitialized) { throw new RuntimeException("lines are not initialized correct"); }
return reader;
}
}
这是一个测试用例(我写信只是为了检查我的模拟生成器):
@Test
public void testImportLines() throws Exception {
CSVReader reader;
List<String> list = new LinkedList<>();
list.add("some,lines,for,testing");
reader = new CSVReaderMock().returnLines(list).create();
System.out.println(reader.readNext()); // should return [Ljava.lang.String;@xxxx with conent-> ["some","lines","for","testing"]
System.out.println(reader.readNext()); // should return null
}
实际输出为:
null
null
所以我的问题是,如何在事先不知道列表外观的情况下传递 return 值的列表?我知道我可以通过 .thenReturn(line1,line2,line3)
传递 "csv lines" 但这会破坏我的方法。
您需要使用 OngoingStubbing 引用将 return 值链接到单个 when() 结果中,例如:
Iterator<String> ls = org.mockito.Mockito.mock(Iterator.class);
OngoingStubbing<String> stubbing = when(ls.next());
for(String s: new String[] { "ABC", "DEF" }) {
stubbing = stubbing.thenReturn(s);
}
System.out.println(ls.next());
System.out.println(ls.next());
... 打印 "ABC",然后 "DEF".
Mockito 有针对这种情况的 ReturnsElementsOf 答案。
Returns elements of the collection. Keeps returning the last element forever. Might be useful on occasion when you have a collection of elements to return.
所以现在您只需要准备元素然后将其传入。因为需要在最后添加 null 调用,所以它会阻止您重用 CSVReaderMock 生成器,但无论是或者你不使用答案。
List<String[]> returnList = new ArrayList<>();
public CSVReaderMock returnLines(@NonNull List<String> lines) {
try {
for (String line : lines) {
String[] lineArr = null;
if (line != null) {
lineArr = line.split(",");
}
returnList.add(lineArr);
}
linesCorrectInitialized = true;
} catch (IOException e) { /* ... */ };
return this;
}
public CSVReader create() {
if (!linesCorrectInitialized) { /* ... */ }
// Return null repeatedly after all stubs are exhausted.
returnList.add(null);
when(reader.readNext()).thenAnswer(new ReturnsElementsOf(returnList));
return reader;
}
我不想嘲笑 CSVReader。所以我的模拟每次都应该 return 一个新数组,这应该是通用的。 最后一个值应该为空。
前
nextLine() -> ["a","b","c"]
nextLine() -> ["a","b","c"]
nextLine() -> null
我的模拟类:
import au.com.bytecode.opencsv.CSVReader;
import com.sun.javafx.beans.annotations.NonNull;
import org.mockito.Mockito;
import org.mockito.stubbing.OngoingStubbing;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class CSVReaderMock {
CSVReader reader;
private boolean linesCorrectInitialized;
public CSVReaderMock()
{
reader = mock(CSVReader.class);
}
public CSVReaderMock returnLines(@NonNull List<String> lines) {
// the last value has to be null
lines.add(null);
try {
for (String line : lines) {
String[] lineArr = null;
if (line != null) {
lineArr = line.split(",");
}
when(reader.readNext()).thenReturn(lineArr);
}
linesCorrectInitialized = true;
} catch (IOException e) {
e.printStackTrace();
};
return this;
}
public CSVReader create() {
if (!linesCorrectInitialized) { throw new RuntimeException("lines are not initialized correct"); }
return reader;
}
}
这是一个测试用例(我写信只是为了检查我的模拟生成器):
@Test
public void testImportLines() throws Exception {
CSVReader reader;
List<String> list = new LinkedList<>();
list.add("some,lines,for,testing");
reader = new CSVReaderMock().returnLines(list).create();
System.out.println(reader.readNext()); // should return [Ljava.lang.String;@xxxx with conent-> ["some","lines","for","testing"]
System.out.println(reader.readNext()); // should return null
}
实际输出为:
null
null
所以我的问题是,如何在事先不知道列表外观的情况下传递 return 值的列表?我知道我可以通过 .thenReturn(line1,line2,line3)
传递 "csv lines" 但这会破坏我的方法。
您需要使用 OngoingStubbing 引用将 return 值链接到单个 when() 结果中,例如:
Iterator<String> ls = org.mockito.Mockito.mock(Iterator.class);
OngoingStubbing<String> stubbing = when(ls.next());
for(String s: new String[] { "ABC", "DEF" }) {
stubbing = stubbing.thenReturn(s);
}
System.out.println(ls.next());
System.out.println(ls.next());
... 打印 "ABC",然后 "DEF".
Mockito 有针对这种情况的 ReturnsElementsOf 答案。
Returns elements of the collection. Keeps returning the last element forever. Might be useful on occasion when you have a collection of elements to return.
所以现在您只需要准备元素然后将其传入。因为需要在最后添加 null 调用,所以它会阻止您重用 CSVReaderMock 生成器,但无论是或者你不使用答案。
List<String[]> returnList = new ArrayList<>();
public CSVReaderMock returnLines(@NonNull List<String> lines) {
try {
for (String line : lines) {
String[] lineArr = null;
if (line != null) {
lineArr = line.split(",");
}
returnList.add(lineArr);
}
linesCorrectInitialized = true;
} catch (IOException e) { /* ... */ };
return this;
}
public CSVReader create() {
if (!linesCorrectInitialized) { /* ... */ }
// Return null repeatedly after all stubs are exhausted.
returnList.add(null);
when(reader.readNext()).thenAnswer(new ReturnsElementsOf(returnList));
return reader;
}