Mockito FindIterable <文档>

时间:2018-06-22 12:28:07

标签: java mockito junit4 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();
        }

Junit:

@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);
}

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

请告知。

谢谢! 巴拉提

3 个答案:

答案 0 :(得分:4)

您在null模拟调用中返回collection.find(...)

FindIterable<Document> findIterable = null;
Mockito.when(collection.find(new Document())).thenReturn(findIterable);

因此,该模拟将在运行时返回null。您需要返回一个FindIterable<Document>对象,该对象允许执行代码进行测试:

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

通过这种方式,您可以断言该方法完成了它设计的目的: 从检索到的FindIterable<Document>中设置名字和姓氏。

您可以使用Mockito.mock()方法来模拟IterableFindIterable<Document>(而使用过的foreach)。
此外,为了不打扰模拟IteratorhasNext()next())的单个方法,这些方法可能会使您的测试难以理解,请使用List(也就是{{ 1}})来填充Iterable,并将模拟的Document的行为委托给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(...);

}

仅当录制中的参数(在Mockito.when(collection.find(new Document())).thenReturn(findIterable); 上)与测试方法在运行时传递的参数匹配时,Mockito才会拦截并替换在模拟程序上调用的方法的行为。
在运行时,以这种方式构建参数:

equals()

因此,模拟录制中的参数应等于上面定义的参数。
请注意,如果BasicDBObject whereClauseCondition = getMetaDataWhereClause(objectId); EmplInfo emplinfo= new EmplInfo (); emplinfo.set_id(objectId); 对于参数类而言不是不可替代/可替代的,则您可以采用以下解决方法:

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

  • 将给定类型的任何对象与equals()匹配。通常是最简单的方法,但不是最可靠的

  • 返回一个Answer而不是要返回的值。那是使用Mockito.any(Class<T>)而不是Mockito.when(...).then(Answer)

答案 1 :(得分:3)

正如您正确指出的那样,您将获得NPE,因为FindIterable为null。您需要模拟一下。
模拟它并不是那么简单,因为它使用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); 

这不是一个完整的解决方案。您需要根据班级进行调整。

答案 2 :(得分:0)

我想用一种通用的方法来模拟pvpkiran(与DistinctIterable相同)来完成FindIterable的答案。

NB /为了避免声纳警告,我使用内部类


    private DistinctIterable<String> mockDistinctIterableString(List<String> resultList) {
        DistinctIterable<String> iterable = mock(DistinctIterableString.class);
        MongoCursor cursor = mock(MongoCursor.class);
        doReturn(cursor)
                .when(iterable).iterator();
        OngoingStubbing<Boolean> whenHasNext = when(cursor.hasNext());
        for (String resultEntry : resultList) {
            whenHasNext = whenHasNext.thenReturn(true);
        }
        whenHasNext.thenReturn(false);
        if (CollectionUtils.isEmpty(resultList)) {
            return iterable;
        }
        OngoingStubbing<Object> whenNext = when(cursor.next());
        for (String resultEntry : resultList) {
            whenNext = whenNext.thenReturn(resultEntry);
        }
        return iterable;
    }

    public interface DistinctIterableString extends com.mongodb.client.DistinctIterable<String> {
    }

使用:

        doReturn(mockDistinctIterableString(asList(
                "entry1",
                "entry2",
                "lastEntry"
        )))
        .when(myRepository)
        .myMongoDistinctQuery();