【问题标题】:One mocked class works fine, the other returns null一个模拟类工作正常,另一个返回 null
【发布时间】:2019-11-05 16:31:14
【问题描述】:

我在我的一个单元测试中模拟 2 个类,用 Mockito 定义行为。何时调用函数。

其中一个模拟类完全按预期工作,另一个返回null。我不知道这两者之间有什么区别。

QueryServiceTest.java

@Import({ QueryServiceTestConfig.class })
@RunWith(SpringRunner.class)
public class QueryServiceTest {

    @Autowired
    private QueryService queryService;
    @MockBean
    private ElasticConnectionService elasticConnectionService;
    @MockBean
    private HBaseConnectionService hbaseConnectionService;

    @Test
    public void test_getRecordsFromQuery() throws IOException {

        // creation of sample data for inputs and outputs goes here

        // This mock works when called from queryService.getRecordsFromQuery()
        when(elasticConnectionService.getRowIdsFromQuery(filterParams, testIndex)).thenReturn(getRowIdsFromQuery_result);

        List<JSONObject> matches = queryService.getMatchingRowIds(getRowIdsFromQuery_result);

        // matchesArray is directly defined to make sure its exactly the same as in queryService.getRecordsFromQuery()
        JSONObject matchesArray = new JSONObject("{\"testTable\":[\"testUUID\"]}");

        // This mock fails when called from queryService.getRecordsFromQuery()
        when(hbaseConnectionService.getRowsByIDs(matchesArray)).thenReturn(getRowsByIDs_result);

        // This returns getRowsByIDs_result as expected
        JSONArray test = hbaseConnectionService.getRowsByIDs(matchesArray);

        // This returns null
        JSONArray actual = new JSONArray(queryService.getRecordsFromQuery(filterParams, testIndex));
    }
}

QueryService.java

@Service
public class QueryService {

    @Autowired
    private ElasticConnectionService elasticConnectionService;
    @Autowired
    private HBaseConnectionService hbaseConnectionService;
    @Autowired
    private PSQLConnectionService psqlConnectionService;

    public String getRecordsFromQuery(
                    Map<String,String> filterParams,
                    String tablename) throws IOException {
        /**
         * Get records that match simple key/value filters
         */

        // This mocked method returns exactly what was expected
        List<List<JSONObject>> lookupsList = elasticConnectionService.getRowIdsFromQuery(filterParams, tablename);

        List<JSONObject> matches = getMatchingRowIds(lookupsList);

        // matchesArray is exactly the same as in the test class
        JSONObject matchesArray = new JSONObject("{\"testTable\":[\"testUUID\"]}");

        // This returns null
        JSONArray hbResults = hbaseConnectionService.getRowsByIDs(matchesArray);

        return hbResults.toString(4);
    }
}

QueryServiceTestConfig.java

@Configuration
public class QueryServiceTestConfig {

    @Bean
    public QueryService queryService() {
        return new QueryService();
    }

    @Bean
    public ElasticConnectionService elasticConnectionService() {
        return new ElasticConnectionService();
    }

    @Bean
    public HBaseConnectionService hbaseConnectionService() {
        return new HBaseConnectionService();
    }

    @Bean
    public PSQLConnectionService psqlConnectionService() {
        return new PSQLConnectionService();
    }
}

最让我困惑的是,在queryService.getRecordsByQuery() 中,elasticConnectionService.getRowIDsFromQuery() 模拟返回了预期的结果,但hbaseConnectionService.getRowsByIDs() 模拟返回了null

elastic 和 hbase 连接服务类都定义在同一个文件夹中,它们唯一的注解是@Service。如果两者都失败,我会认为我配置了一些错误,但 elasticConnectionService 调用按预期工作的事实告诉我正在发生其他事情。

【问题讨论】:

  • JSONObject的封装是什么?
  • @StvnBrkdll 我正在使用 org.json

标签: java spring unit-testing testing mockito


【解决方案1】:

如果JSONObject的包是org.json,那么JSONObject的equals方法如下:

public boolean equals(Object object) {
            return object == null || object == this;
        }

由于QueryServicematchesArray 的实例与QueryServiceTest 中的实例不同,所以equals() 方法将返回false。

尝试改变这个:

when(hbaseConnectionService.getRowsByIDs(matchesArray)).thenReturn(getRowsByIDs_result);

到这里,看看你的结果是否改变:

when(hbaseConnectionService.getRowsByIDs(Mockito.any())).thenReturn(getRowsByIDs_result);

我想你也可以这样做:

when(hbaseConnectionService.getRowsByIDs(Mockito.eq(matchesArray))).thenReturn(getRowsByIDs_result);

或:

when(hbaseConnectionService.getRowsByIDs(Matchers.eq(matchesArray))).thenReturn(getRowsByIDs_result);

因为在底层,Matchers.eq() 方法可能调用了JSONObject.equals(),Matcher 可能无法工作(我没有检查Matchers.eq() 的源代码)。

通常,在设置模拟方法调用时,您希望将 parameter(s) 包装在 Mockito 的 Matcher 方法之一中。不幸的是,这不适用于您的方案。

(请注意,Mockito 类扩展了 Matchers)

【讨论】:

  • 谢谢!我可以使用any(),在这种情况下没问题,但我尝试使用eq(matchesArray),但仍然不匹配。在这种情况下,我可以使用any(),但如果可能的话,我更愿意对我的测试进行更具体的测试,有没有办法让JSONObject 在 Mockito 上表现得像预期的那样?
  • 由于 JSONObject 的 equals() 方法的实现方式,不,你不能让它按照你希望的方式运行(即你不能使用 Matchers)
  • 为了使您的代码更易于测试和更灵活,请考虑将matchesArray 作为参数传递给getRecordsFromQuery()。这将允许您在单元测试中使用 Matchers,同时也使您的方法更加灵活。
  • 好吧,在我去挖掘一个新的 JSON 库之前,有没有你推荐的可以很好地与 Mockito 配合使用的库?
  • 杰克逊的故事
猜你喜欢
  • 2019-08-18
  • 2022-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多