Mockito FindIterable<Document>

Bha*_*a S 7 java junit4 mockito mongodb-java

我正在尝试为以下方法编写 JUnit 测试用例,我正在使用 Mockito 框架。

方法:

public EmplInfo getMetaData(String objectId) {

        objectId = new StringBuffer(objectId).reverse().toString();
        try{
            BasicDBObject whereClauseCondition = getMetaDataWhereClause(objectId);
            EmplInfo emplinfo= new EmplInfo ();
            emplinfo.set_id(objectId);
            FindIterable<Document> cursorPersonDoc = personDocCollection.find(whereClauseCondition);
            for (Document doc : cursorPersonDoc) {
                emplinfo.setEmplFirstName(doc.getString("firstname"));
                emplinfo.setEmplLastName(doc.getString("lastname"));
                break;
            }
            return emplinfo;
        }catch(Exception e){
         e.printstacktrace();
        }
Run Code Online (Sandbox Code Playgroud)

朱尼特:

@Test
public void testGetMetaData() throws Exception {
    String docObjectId = "f2da8044b29f2e0a35c0a2b5";
    BasicDBObject dbObj = personDocumentRepo.getMetaDataWhereClause(docObjectId);
    FindIterable<Document> findIterable = null;
    Mockito.when(collection.find(dbObj)).thenReturn(findIterable);
    personDocumentRepo.getMetaData(docObjectId);
}
Run Code Online (Sandbox Code Playgroud)

在“personDocumentRepo.getMetaData(docObjectId)”中得到空点期望,因为我“返回”了空的findIterable。不确定如何将虚拟/测试值分配给 findIterable。

请指教。

谢谢!婆罗提

pvp*_*ran 6

正如您正确指出的那样,您正在获得 NPE,因为 FindIterable 为空。你需要嘲笑它。模拟
它不是那么简单,因为它使用MongoCursor(这反过来扩展Iterator),您需要模拟某些内部使用的方法。

在遍历 Iter 的某些方法时

我相信你必须做这样的事情。

FindIterable iterable = mock(FindIterable.class);
MongoCursor cursor = mock(MongoCursor.class);

Document doc1= //create dummy document;
Document doc2= //create dummy document;

when(collection.find(dbObj)).thenReturn(iterable);

when(iterable.iterator()).thenReturn(cursor);
when(cursor.hasNext()) 
  .thenReturn(true)
  .thenReturn(true)// do this as many times as you want your loop to traverse
 .thenReturn(false); // Make sure you return false at the end.
when(cursor.next())
  .thenReturn(doc1)
  .thenReturn(doc2); 
Run Code Online (Sandbox Code Playgroud)

这不是一个完整的解决方案。你需要让它适应你的班级。


dav*_*xxx 5

nullcollection.find(...)模拟调用返回:

FindIterable<Document> findIterable = null;
Mockito.when(collection.find(new Document())).thenReturn(findIterable);
Run Code Online (Sandbox Code Playgroud)

所以模拟会null在运行时返回。您需要的是返回一个FindIterable<Document>对象,该对象允许执行与以下内容相关的测试代码:

for (Document doc : cursorPersonDoc) {
    emplinfo.setEmplFirstName(doc.getString("firstname"));
    emplinfo.setEmplLastName(doc.getString("lastname"));
    break;
}
return emplinfo;
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以断言该方法执行其设计目的:设置检索到的名字和姓氏FindIterable<Document>

您可以使用该Mockito.mock()方法来模拟FindIterable<Document>它是一个Iterable(而使用的foreach)。
此外,为了避免模拟Iterator( hasNext(), next()) 的各个方法(这可能会降低测试的可读性),请使用 a List(也是一个Iterable)来填充Documents 并将模拟的行为委托FindIterable.iterator()List.iterator()

@Test
public void testGetMetaData() throws Exception {
  ... 
  // add your document instances
  final List<Document> documentsMocked = new ArrayList<>();
  documentsMocked.add(new Document(...));
  documentsMocked.add(new Document(...));

  // mock FindIterable<Document>
   FindIterable<Document> findIterableMocked = (FindIterable<Document>) Mockito.mock(FindIterable.class);

  // mock the behavior of FindIterable.iterator() by delegating to List.iterator()
  when(findIterableMocked.iterator()).thenReturn(documentsMocked.iterator());

  // record a behavior for Collection.find()
  Mockito.when(collection.find(dbObj)).thenReturn(findIterableMocked);

  // execute your method to test
  EmplInfo actualEmplInfo = personDocumentRepo.getMetaData(...);

  // assert that actualEmplInfo has the expected state
  Assert(...);

}
Run Code Online (Sandbox Code Playgroud)

我想补充一点,这样的模拟可能行不通:

Mockito.when(collection.find(new Document())).thenReturn(findIterable);
Run Code Online (Sandbox Code Playgroud)

仅当记录中的参数与测试方法在运行时传递的参数匹配(根据equals())时,Mockito 才会拦截并替换在模拟上调用的方法的行为。
在运行时,参数以这种方式构建:

BasicDBObject whereClauseCondition = getMetaDataWhereClause(objectId);
EmplInfo emplinfo= new EmplInfo ();
emplinfo.set_id(objectId);
Run Code Online (Sandbox Code Playgroud)

因此,模拟记录中的参数应该等于上面定义的参数。
请注意,如果equals()参数类不可覆盖/可覆盖,您可以采取一些解决方法,例如:

  • 将对象作为参数传递到方法中进行测试(需要一些重构)。在这种情况下,模拟参数和在运行时在要测试的方法中传递的引用必须相等,因为它们引用相同的对象

  • 将给定类型的任何对象与Mockito.any(Class<T>). 通常是最简单的方法,但不是最可靠的方法

  • 返回 anAnswer而不是要返回的值。那是使用Mockito.when(...).then(Answer)而不是Mockito.when(...).thenReturn(valuetoReturn)

  • 谢谢大卫!我们可以选择设置 FindIterable&lt;Document&gt; findIterable; 有一些虚拟值? (2认同)