Ev0oD
Ev0oD

Reputation: 1861

Mockito test failure - arguments (Mongo DBObjects) are different (are not!)

EDIT: the question advanced into a more specific one below.

A test doesn't pass, this failure occurs:

Argument(s) are different! Wanted:
declColl.find(
    { "declensions" : { "$in" : [ "testtest"]}}
);
-> at cz.xxx.CzechMongoWordDeclensionsRetrieverTest.getLemmaTest(CzechMongoWordDeclensionsRetrieverTest.java:63)
Actual invocation has different arguments:
declColl.find(
    { "declensions" : { "$in" : [ "testtest"]}}
);

You can see what my problem is immediately from the output of the failure. There is no difference. The toString method might be deceiving, maybe equals method returns false for the DBObjects... as I state below, I am not sure how to check this, but checking equality of two "queryBuilt" same DBObjects returned true.

The test is short and looks like this:

@InjectMocks
private CzechMongoWordDeclensionsRetriever declRetriever;
@Mock
private DBCollection declColl;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
    Object[] criteria = new String[1];
    criteria[0] = "testtest";

    //mocks init
    DBCursor cur = mock(DBCursor.class);
    DBObject queryObj = QueryBuilder.start("declensions").in(criteria).get();
    when(declColl.find(queryObj)).thenReturn(cur);
    when(cur.size()).thenReturn(1);
    when(cur.next()).thenReturn(new BasicDBObject("lemma", "testtest"));
}

@Test
public void getLemmaTest() {
    Object[] criteria = new String[1];
    criteria[0] = "testtest";
    DBObject queryObj = QueryBuilder.start("declensions").in(criteria).get();

    String toTest = "testtest";
    String testResult = declRetriever.getLemma(toTest);

    verify(declColl).find(queryObj);
}

with the getLemma method like this:

public String getLemma(String word) {
        criteria[0] = word;
        DBObject lemmaObj = QueryBuilder.start("declensions").in(criteria).get();
        DBCursor cursor = declColl.find(lemmaObj);

        if(cursor == null)     return null;
        if(cursor.size() > 1)  return null; 
        if(cursor.size() == 0) return null; 

        return (String) cursor.next().get("lemma");
    }

The code doesn't even get lower than to if(cursor == null) part, since the stub is not working. I created two distinct DBObjects by running the QueryBuilder twice and then checked if they are equal (I don't know how the implementation of equals Mongo DBObject lookslike - edit: compares map entries).

I have no clue, why the failure occurs.

EDIT: (should this be a new question?)

I have tried Biju Kunjummen's answer and it did not help at first, but I tried this:

    Object[] criteria = new String[1];
    criteria[0] = "testtest";
    DBObject queryObj = QueryBuilder.start("declensions").in(criteria).get();
    Object[] criteria2 = new String[1];
    criteria2[0] = "testtest";
    DBObject queryObj2 = QueryBuilder.start("declensions").in(criteria).get();
    assertTrue(queryObj.equals(queryObj2));

and the assert fails. So I can see the problem is not with Mockito, but the DBObjects do not match, even though they should. To me, semantically the queries are equal. (answered below): it seems like, if a Map.entry has an Object[] as a value which has the same contents as the compared Object[] equals goes false, if the Object[] is not the same reference as the compared one.

Upvotes: 0

Views: 1417

Answers (2)

Ev0oD
Ev0oD

Reputation: 1861

So: verify method was checking, if I was using the same queryObj as attribute. Since it used equals and since DBObject is compared by the mapped values via Map equals method, it checked the equality of Map entrys. And I believe, that it did not compare the two Object[] arrays for it's contents, but checked the references and found them not matching. Thus equality was false and verify said that it was expecting different parameter.

I replaced the use of Object[] with List<> and now the verify method is happy - it passes.

The test was still not working, but it was for completely different reasons, which I (might) describe in a different question: //will post here, if I find looking for an answer important enough.

Anyways, the test now works ok like this:

@Test
public void getLemmaTest() {

    //... now I use List<String> instead of Object[]. declColl is already stubbed 
    // to return something, when a find(queryObj) is called.
    List<String> criteriaColl = new ArrayList<>();
    criteriaColl.add("testtest");
    DBObject queryObj = QueryBuilder.start("declensions").in(criteriaColl).get();

    String expResult = "testtest";
    String toTest = "testtest";
    String testResult = declRetriever.getLemma(toTest);

    verify(declColl).find(queryObj);
    assertEquals(expResult, testResult);  
}

and the implementation of getLemma.

public String getLemma(String word) {
    criteria.set(0, word);
    DBObject lemmaObj = QueryBuilder.start("declensions").in(criteria).get();
    DBCursor cursor = declColl.find(lemmaObj);
    if(cursor == null)      return null;
    if(cursor.size() > 1)   return null;
    if(cursor.size() == 0)  return null;
    return (String) cursor.next().get("lemma");
}

I could not use

criteria.add(word);
//....
criteria.remove(0);

for unknown reasons, which I (might) ask in the next question. (I will post the link here, if I create the question)

Upvotes: 1

Biju Kunjummen
Biju Kunjummen

Reputation: 49935

Possibly because your DBObject.equals(and hashcode) is not correctly implemented, a quick way to check it would be to try this:

DBObject queryObj1 = QueryBuilder.start("declensions").in(criteria).get();
DBObject queryObj2 = QueryBuilder.start("declensions").in(criteria).get();

assertTrue(queryObj1.equals(queryObj2))

This would likely fail, and this is the reason for the Mock assertion error.

Upvotes: 1

Related Questions