Hi guys, I've also faced the same issue and could resolve it. Actually there're two ways - using Robolectric shadows and mocks. I've used both of them - shadow for UsbManager and easyMock for UsbAccessory classes. So, first of all you need your production code could get non null UsbManager object. To make this possible you need to:
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.internal.ShadowExtractor;
@Implements(UsbManager.class)
public class ShadowUsbManager {
private UsbAccessory[] accessoryList = new UsbAccessory[1];
private boolean accessoryListRequested = false;
public static ShadowUsbManager shadowOf(UsbManager actual) {
return (ShadowUsbManager) ShadowExtractor.extract(actual);
}
public boolean wasAccessoryListRequested() {
return accessoryListRequested;
}
public void attachAccessory(UsbAccessory accessory) {
accessoryList[0] = accessory;
}
@Implementation
public UsbAccessory[] getAccessoryList() {
accessoryListRequested = true;
return accessoryList;
}
}
This implementation offers very limited functionality since I've just started writing tests but it yet let's you to add the methods called from your production code and needed in test methods.
2)You need to configure Robolectric test runner to use your custom shadow during test cycle - modify your test class' annotation in the following way:
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21, shadows={ShadowUsbManager.class})
3) Since ShadowContextImpl's getSystemService() (which shadows real Android getSystemService() called during real runs, not test ones) knows nothing about Context.USB_SERVICE by default (and actually it is this method which complains about "Unknown service usb") you need to set this service before calling the production code which gets this service:
private Object setUsbSystemService() throws ClassNotFoundException {
Class<?> usbManagerClazz = Class.forName("android.hardware.usb.UsbManager");
assertNotNull(usbManagerClazz);
Class<?> iUsbManagerClazz = Class.forName("android.hardware.usb.IUsbManager");
assertNotNull(iUsbManagerClazz);
Object usbManager = ReflectionHelpers.callConstructor(usbManagerClazz,
ReflectionHelpers.ClassParameter.from(Context.class, RuntimeEnvironment.application),
ReflectionHelpers.ClassParameter.from(iUsbManagerClazz, null));
ShadowContextImpl shadowContext = (ShadowContextImpl) shadowOf(RuntimeEnvironment.application.getBaseContext());
shadowContext.setSystemService(Context.USB_SERVICE, usbManager);
return usbManager;
}
@Before
public void SetupService() throws ClassNotFoundException {
Object usbManager = setUsbSystemService();
service = Robolectric.setupService(MyService.class); //MyService.OnCreate() calls getSystemService()
assertNotNull(service.getUsbManager());
assertSame("Service got not the same UsbManager object that expected", usbManager, service.getUsbManager());
shadowUsbManager = ShadowUsbManager.shadowOf(service.getUsbManager());
}
So now we can test the code which obtains UsbManager. But there's also UsbAccessory class (and some others) which are used by the manager (e.g. UsbAccessory[] is returned by UsbManager.getAccessoryList()). Problem is that you can't just create new UsbAccessory because of it's access modificators. So I've just create it's mock and pass it to ShadowUsbManager:
UsbAccessory accessory = createNiceMock(UsbAccessory.class); // this method of easyMock lib creates simple mock of UsbAccessory class
assertNotNull(accessory);
shadowUsbManager.attachAccessory(accessory);
So I hope I've gave you a clue to how this kind of problems can be solved. Regards from Russia :)
Boris.
среда, 25 июня 2014 г., 20:18:24 UTC+3 пользователь Arpita написал: