我正在编写一个单元测试,如果在我的twilio帐户上已经找到子帐户,则返回true的方法。我试图用Mockito模拟这个,但得到cast错误,将列表转换为AccountList。我浏览了mockito文档,但可能遗漏了一些东西。
这是测试:
@Mock
TwilioRestClient client;
@Mock
AccountList accountList;
@Mock
Iterator<Account> iterator;
@Mock
Account account;
@Test
public void testShouldReturnTrueIfAccountNameFound() {
final List<Account> list = Arrays.asList(account);
when(client.getAccounts()).thenReturn((AccountList) list);
when(account.getFriendlyName()).thenReturn("test");
when(accountList.iterator()).thenReturn(list.iterator());
MyTwilioAccountStore store = null;
store = new MyTwilioAccountStore(client);
Assert.assertTrue(store.subAccountExists("test"));
}
这就是我要测试的方法。我在构造函数中注入TwilioRestClient。
/**
* class constructor
*
* @param client
*/
public MyTwilioAccountStore(TwilioRestClient client) {
fClient = client;
}
/**
* rest client getter
*
* @return RESTClient
*/
public TwilioRestClient getRestClient() {
return fClient;
}
/**
* Check if a sub account already exists
*
* @param friendlyName
* @return boolean
*/
public boolean subAccountExists(String friendlyName) {
// Build a filter for the AccountList
Map<String, String> params = new HashMap<String, String>();
params.put("FriendlyName", friendlyName);
AccountList accounts = getRestClient().getAccounts(params);
// Loop over accounts
// This is where I get NPE
for (Account account : accounts) {
if (account.getFriendlyName().equalsIgnoreCase(friendlyName)) {
return true;
}
}
return false;
}
这是Twilio源代码的getAccounts:
/**
* Get all accounts. For more info: {@link <a
* href="http://www.twilio.com/docs/api/rest/account"
* >http://www.twilio.com/docs/api/rest/account</a>}
*
* @return the list of accounts.
*/
public AccountList getAccounts() {
return this.getAccounts(new HashMap<String, String>());
}
如何正确模拟AccountList ?
在我看来你做错了。仅仅因为您在一个字段上说了@Mock
,并不意味着任何实例化相同类型的类都会得到它。您需要找到一种方法让getRestClient()
返回您的模拟。如果它返回一个new AccountList
,那将不是您的mock,并且它可能会导致这些问题。
告诉我们getRestClient()
方法,我可以帮助更多。
好的,你有一个getAccounts()
方法,你用这行来模拟它:
when(client.getAccounts()).thenReturn((AccountList) list);
但是被测系统调用这一行:
AccountList accounts = getRestClient().getAccounts(params);
看到区别了吗?实际被调用的方法接受一个Map
,但是你没有嘲笑那个方法。你需要把上面的when改成这样:
when(client.getAccounts(any(Map.class))).thenReturn((AccountList) list);
换句话说,你嘲弄了错误的方法。默认情况下,Mockito将返回合理的默认值,在这种情况下,它恰好是null
。如果getAccounts()
返回一个Integer/int,它将返回0。
EDIT似乎这里真正的问题是能够模拟AccountList
,从twilio客户端返回…
EDIT continue 如果,正如我猜想的那样,该类实现了Iterable<Account>
,那么需要的是用模拟Account
构建一个单元素列表,并让accountList.iterator()
返回上面创建的列表的Iterator
原始回答
你模拟:
when(accountList.iterator().next())
但是当您使用foreach循环时,首先调用的是.iterator()
本身—并且您没有mock。作为返回,mockito执行默认操作:返回null
(说实话,我真的很惊讶,它甚至没有抛出一个异常,告诉"对不起,不能这样做",因为它是一个链式方法调用!)。
你应该:
when(accountList.iterator()).thenReturn(anIterator);
其中anIterator
是一个实际的迭代器,包括.hasNext()
、.next()
和.remove()
。
既然你似乎也想为你的帐户使用mock,我猜你的列表应该这样设置:
final List<Account> accountList = Arrays.asList(account);
给了:
@Test
public void shouldReturnTrueIfAccountNameFound()
{
final List<Account> list = Arrays.asList(account);
when(account.getFriendlyName()).thenReturn("test");
when(accountList.iterator()).thenReturn(list.iterator());
final MyTwilioAccountStore store = new MyTwilioAccountStore(client);
Assert.assertTrue(store.subAccountExists("test"));
}