使用 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;
}