I am trying to test my activity using either ActivityUnitTestCase or ActivityInstrumentationTestCase2. In the activity I want to test my
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
menu.setHeaderTitle(R.string.lcMenuHeader);
menu.add(Menu.NONE, EDIT_MENU_POS, EDIT_MENU_POS, R.string.lcEditAccount);
menu.add(Menu.NONE, COPY_MENU_POS, COPY_MENU_POS, R.string.lcCopyAccount);
menu.add(Menu.NONE, DELET开发者_StackOverflow中文版E_MENU_POS, DELETE_MENU_POS, R.string.lcDeleteAccount);
}
How can I test that the menu is in the right state - ie. 3 items and a given header? I can trigger it being created in my ActivityInstrumentationTestCase2 test using
textView.performLongClick();
But I don't know how to get it back.
Thanks Stephen
In the end, I used reflection with the ActivityUnitTestCase test case:
AccountEntryContextMenuMock mock = new AccountEntryContextMenuMock();
ContextMenu menu = (ContextMenu)Proxy.newProxyInstance(mLogInActivity.
getClassLoader(), new Class<?>[] { ContextMenu.class }, mock);
mLogInActivity.onCreateContextMenu(menu, accList, null);
//Now query the mock that the right things happened
With the mock menu looking like:
public class AccountEntryContextMenuMock implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().equals("setHeaderTitle")) {
//record it and make available for test
}
else if (method.getName().equals("add")) {
//record it and make available for test
}
return null;
}
}
Finally I have found a way to truly test the content of context menu's and action bar items as well.
As pre-requisite you will need create a special android platform which does inclused some internal android classes which are otherwise not available. Inazaruk wrote an excellent tutorial how to do this.
For testing the context menu and action bar items we will use following classes:
- ANDROID_SDK/sources/android-VERSION/com/android/internal/view/menu/MenuBuilder
- ANDROID_SDK/sources/android-VERSION/com/android/internal/view/menu/MenuItemImpl
Please be aware that internal classes can be changed any time. This can result in failures of this unit test!
For accessing private fields in those classes we wil use a helper function:
/**
* Get the value for field using relfection calls. This can be used to
* access private fields in objects.
*
* @param sourceObject
* : the object containing the field
* @param fieldName
* : the name of the field from which the value has to be
* retrieved
* @return value of the field
*/
private Object getValueForDeclaredField(Object sourceObject,
String fieldName) {
// Check parameters
if (sourceObject == null) {
throw new RuntimeException(
"Error in function getValueForDeclaredField. "
+ "Source object can not be null.");
}
if (fieldName == null || fieldName.trim().isEmpty()) {
throw new RuntimeException(
"Error in function getValueForDeclaredField. "
+ "Fieldname can not be null or empty.");
}
// Get field definition from source
Field field = null;
try {
field = sourceObject.getClass().getDeclaredField(fieldName);
} catch (Exception e) {
throw new RuntimeException(
"Error in function getValueForDeclaredField. Field '"
+ fieldName
+ "' does not exist in class '"
+ sourceObject.getClass().getName()
+ "'. Android implementation may have been changed. "
+ "Please check this reflection.");
}
field.setAccessible(true);
Object targetObject = null;
try {
targetObject = field.get(sourceObject);
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Error in function getValueForDeclaredField. Value for field '"
+ fieldName
+ "' in class '"
+ sourceObject.getClass().getName()
+ "' can not be retrieved. "
+ "Android implementation may have been changed. "
+ "Please check this reflection.");
}
return targetObject;
}
Using the helper function above we can now check the content of the context menu of a view with following code.
final int EXP_NUMBER_OF_CONTEXT_ITEMS = 1;
final String EXP_CONTEXT_ITEM_0_TITLE = "YOUR TITLE FOR CONTEXT ITEM 0";
// As the contextMenu is build dynamically each time it is needed, we
// have to build the context menu in order to be able to test its
// contents.
ContextMenuBuilder contextMenuBuilder = new ContextMenuBuilder(
mActivity);
assertNotNull("Context menu is found", contextMenuBuilder);
// Function contextMenuBuilder.show has to be called in order to
// dynamically add the contents for the selected view.
// Notes:
// - the context menu will not be shown actually.
// - callbacks have not been attached to this menu.
MenuDialogHelper contextMenuDialogHelper = contextMenuBuilder.show(
YOUR_VIEW_HERE, view.getWindowToken());
assertNotNull("ContextMenuDialogHelper is created",
contextMenuDialogHelper);
// Retrieve the menu from the Menu Builder.
ContextMenuBuilder menu = (ContextMenuBuilder) getValueForDeclaredField(
contextMenuDialogHelper, "mMenu");
assertNotNull("ContextMenu is available", menu);
// Retrieve and test items in the context menu
assertEquals("Number of items in context menu",
EXP_NUMBER_OF_CONTEXT_ITEMS, menu.size());
assertEquals("Title of item 0", EXP_CONTEXT_ITEM_0_TITLE,
menu.getItem(0).toString());
// Cleanup the conetxtMenuDiaglogHelper.
contextMenuDialogHelper.dismiss();
Using the helper function above we can now check the content of action bar en activity menu with following code.
/*
* IMPORTANT NOTICE: This test uses internal classes which are not available
* in the default "android.jar" file. Please refer to link mentioned above
* for creating this file.
*
* After creating a version of "android.jar" including interal classes your
* unit-test-project should be targeted to this specific android version.
*/
public void testActionBar() {
final String EXP_TITLE_ACTION_BAR = "YOUR ACTION BAR TITLE HERE";
final int EXP_NUMBER_OF_ACTION_ITEMS = 2;
final String EXP_ACTION_0_TITLE = "YOUR TITLE FOR ACTION BAR ITEM 0";
final String EXP_ACTION_1_TITLE = "YOUR TITLE FOR ACTION BAR ITEM 1";
ActionBar actionBar = mActivity.getActionBar();
assertNotNull("Action bar is defined", actionBar);
// Other assertiong for actionBar here
// Retrieve the action view for the action bar.
ViewGroup mActionView = (ViewGroup) getValueForDeclaredField(actionBar,
"mActionView");
assertNotNull("Action view exists", mActionView);
// Check if context menu exists for the action view
MenuBuilder mOptionsMenu = (MenuBuilder) getValueForDeclaredField(
mActionView, "mOptionsMenu");
assertNotNull("Options menu exists", mOptionsMenu);
// Check action items for the action view
ArrayList<MenuItemImpl> mActionItems = (ArrayList<MenuItemImpl>)
getValueForDeclaredField(mOptionsMenu, "mActionItems");
assertEquals("Number of action items", EXP_NUMBER_OF_ACTION_ITEMS,
mActionItems.size());
// Check title and visibility of first action item
MenuItemImpl action = mActionItems.get(0);
assertEquals("Title of action item 0", EXP_ACTION_0_TITLE,
action.getTitle());
assertTrue("Action item 0 is visible", action.isVisible());
// other assertions for first action item here
// Check title and visibility of second action item
// Item will only be shown if room, so do not assert that it is shown.
action = mActionItems.get(1);
assertEquals("Title of action item 1", EXP_ACTION_1_TITLE,
action.getTitle());
// other assertions for second action item here
// Check non action items for the action view
ArrayList<MenuItemImpl> mNonActionItems = (ArrayList<MenuItemImpl>)
getValueForDeclaredField(mOptionsMenu, "mNonActionItems");
assertEquals("Number of non action items",
mNonActionItems.size(), 1);
// other assertions for non action items here
}
精彩评论