In my Android application I have set up Volley.
Robolectric.application is initialized and all other tests runs smoothly.
I get this error when trying to get mocked HTTP response.
This is my test:
#RunWith(MyRobolectricTestRunner.class)
public class ApiTests {
#Inject
protected Api api;
#Before
public void setUp() {
ObjectGraph.create(new AndroidModule(Robolectric.application), new TestApplicationModule()).inject(this);
}
#Test
public void shouldGetErrorList() throws Exception {
Project project = new Project("test", "test", "test", DateTime.now());
addPendingProjectsErrorsResponse("response.json"); //adding response to FakeHttpLayer
api.getProjectErrors(project, new Listener<ProjectErrors>() {
#Override
public void onResponse(ProjectErrors response) {
assertNotNull(response);
}
}, new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
throw new RuntimeException(error);
}
}
);
}
}
This is error I get:
Exception in thread "Thread-3" java.lang.NullPointerException
at org.robolectric.shadows.ShadowLooper.getMainLooper(ShadowLooper.java:59)
at android.os.Looper.getMainLooper(Looper.java)
at org.robolectric.Robolectric.getUiThreadScheduler(Robolectric.java:1301)
at org.robolectric.shadows.ShadowSystemClock.now(ShadowSystemClock.java:15)
at org.robolectric.shadows.ShadowSystemClock.uptimeMillis(ShadowSystemClock.java:25)
at org.robolectric.shadows.ShadowSystemClock.elapsedRealtime(ShadowSystemClock.java:30)
at android.os.SystemClock.elapsedRealtime(SystemClock.java)
at com.android.volley.VolleyLog$MarkerLog.add(VolleyLog.java:114)
at com.android.volley.Request.addMarker(Request.java:174)
at com.android.volley.CacheDispatcher.run(CacheDispatcher.java:92)
I had same error and avoid it by using my own (and ugly) SystemClock shadow.
shadow class:
#Implements(value = SystemClock.class, callThroughByDefault = true)
public static class MyShadowSystemClock {
public static long elapsedRealtime() {
return 0;
}
}
test code:
#Test
#Config(shadows = { MyShadowSystemClock.class, ... })
public void myTest() {
}
Another workaround would be to disable Volley logging by calling
VolleyLog.DEBUG = false;
in your setUp method.
Related
I have following Service interface for retrofit :
public interface TimetableService {
#GET("url/")
Call<Flixbus> getTimetable();
}
I want to test UI after retrofit call :
#RunWith(AndroidJUnit4.class)
public class TestTimetablePresenter {
private static List<Departure> DEPARTURES = Lists.newArrayList(
new Departure("L006", "Stockholm",
new Datetime(1461056400, "GMT+02:00")),
new Departure("L007", "Berlin",
new Datetime(1461056500, "GMT+02:00")));
#Mock
private TimetableContract.View mTimetableView;
#Mock
private TimetableService mockTimetableServiceImpl;
#Captor
private ArgumentCaptor<Callback<Flixbus>> mCallbackArgumentCaptor;
private TimetablePresenter mNotesPresenter;
#Before
public void setupNotesPresenter() {
MockitoAnnotations.initMocks(this);
SampleApp mApp = (SampleApp) InstrumentationRegistry.getInstrumentation()
.getTargetContext().getApplicationContext();
mNotesPresenter = new TimetablePresenter(mApp, mTimetableView);
mNotesPresenter.setWebService(mockTimetableServiceImpl);
}
#Test
public void loadDeparturesFromAPIAndLoadIntoView() {
mNotesPresenter.loadDepartures();
Mockito.verify(mockTimetableServiceImpl).getTimetable().enqueue(mCallbackArgumentCaptor.capture());
InOrder inOrder = Mockito.inOrder(mTimetableView);
inOrder.verify(mTimetableView).setProgressIndicator(true);
inOrder.verify(mTimetableView).setProgressIndicator(false);
Mockito.verify(mTimetableView).showDepartures(DEPARTURES);
}
}
But at this line, I get an error:
Mockito.verify(mockTimetableServiceImpl).getTimetable().enqueue(mCallbackArgumentCaptor.capture());
Error says:
java.lang.NullPointerException: Attempt to invoke interface method 'retrofit2.Call retrofit2.Call.clone()' on a null object reference at com.ali.android.sample.timetable.TimetablePresenter.loadDepartures(TimetablePresenter.java:46)
I can't figure out how to solve it. Do you have any suggestion?
#Override
public void loadDepartures() {
mTimetableView.setProgressIndicator(true);
service.getTimetable().clone().enqueue(new Callback<Flixbus>() {
#Override
public void onResponse(Call<Flixbus> call, Response<Flixbus> response) {
Log.d(TAG, "response is successful :" + response.isSuccessful());
mTimetableView.setProgressIndicator(false);
if(response.isSuccessful()) {
mTimetableView.showDepartures(response.body().getTimetable().getDepartures());
} else {
Log.d(TAG, "Response Code: " + response.code() + "Message: " + response.message());
}
}
#Override
public void onFailure(Call<Flixbus> call, Throwable t) {
mTimetableView.setProgressIndicator(false);
Log.e(TAG, t.getLocalizedMessage());
}
});
}
Have you tried switching the order of verification? Usually mock verification comes after interaction with class under test.
// I believe TimetableService#getTimetable().enqueue(...) is called inside TimetablePresenter#loadDepartures()
mNotesPresenter.loadDepartures();
Mockito.verify(mockTimetableServiceImpl).getTimetable().enqueue(mCallbackArgumentCaptor.capture());
I have MVP in my application. Presenter has interface
public interface ILoginPresenter<V> extends Presenter<V> {
void logUserIn(String email, String password, String deviceToken, String deviceType);
}
Realization has RX Single
mLoginSubscription = mModel.logUserIn(email, password, deviceToken, deviceType)
.compose(RxUtil.setupNetworkSingle())
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
Timber.i("Log in complete");
}
#Override
public void onError(Throwable e) {
Timber.e(e, "Retrofit could not get User.");
getView().dismissProgressDialog();
}
#Override
public void onNext(UserResponseRetrofit response) {
Timber.i("Retrofit is attempting to get User");
mSaveModel.saveUser(user);
getView().dismissProgressDialog();
getView().goToMenuActivity();
}
});
Also i have module for Dagger
#Module
public class ModelModule {
#Provides
#ScreenScope
public ILoginModel provideLoginModel(LoginModel p) {
return p;
}
}
My unit test look like next:
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21, manifest = "/src/main/AndroidManifest.xml")
public class LoginPresenterTest {
public static final String SOME_OTHER_TOKEN = "someOtherToken";
private AppComponent mAppComponent;
private LoginComponent mLoginComponent;
private ILoginView mockView;
private ModelModule mockModel;
private ILoginPresenter mLoginPresenter;
#Before
public void setup() {
// Creating the mocks
mockView = Mockito.mock(ILoginView.class);
mockModel = Mockito.mock(ModelModule.class);
ILoginModel mock = Mockito.mock(ILoginModel.class);
User urr = Mockito.mock(User.class);
Mockito.when(mockModel.provideLoginModel(null)).thenReturn(mock);
Mockito.when(mock.logUserIn("", "", "", "")).thenReturn(ScalarSynchronousSingle.just(urr));
mAppComponent = DaggerAppComponent.builder()
.appModule(new AppModule(RuntimeEnvironment.application))
.build();
mLoginComponent = DaggerLoginComponent.builder()
.appComponent(mAppComponent)
.modelModule(mockModel)
.presenterModule(new PresenterModule())
.build();
mLoginPresenter = mLoginComponent.provideLoginPresenter();
mLoginPresenter.setView(mockView);
}
#Test
public void testLogin() {
mLoginPresenter.logUserIn("", "", "", "");
try {
java.lang.Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Mockito.verify(mockView).dismissProgressDialog();
}
So using Dagger I need to build Presenter correctly. For this purposes, I am trying to use Mockito.when. Firstly look like this line doesn't work
Mockito.when(mockModel.provideLoginModel(null)).thenReturn(mock);
The target purpose is to use my own Model realization which return Single.
don't really understand why my mock of ModelModule doesn't work?
What about creating a test module out of your production Module?
See how they suggest to do testing via Dagger in official site.
#Module
public class ModelModuleTest extends ModelModule {
#Override
public ILoginModel provideLoginModel(LoginModel p) {
...
}
}
You can pass mocked dependency to your Module.
UPDATED
The issue may be that you are mocking will null. In that case mockModel will only return mock when provideLoginModel is called with null
Mockito.when(mockModel.provideLoginModel(null)).thenReturn(mock);
mockModel.provideLoginModel(null) // returns mock
mockModel.provideLoginModel(new Foo()) // returns null
Instead you can use a matcher such as any():
Mockito.when(mockModel.provideLoginModel(any())).thenReturn(mock);
mockModel.provideLoginModel(null) // returns mock
mockModel.provideLoginModel(new Foo()) // also returns null
to return mock on any call.
BIG PICTURE
For unit testing I would suggest not using Dagger, instead use #Mock and #InjectMocks you only need the object you are testing to be real the rest can be mocks.
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21, manifest = "/src/main/AndroidManifest.xml")
public class LoginPresenterTest {
public static final String SOME_OTHER_TOKEN = "someOtherToken";
#Mock
ILoginView mockView;
#Mock
SomePresenterDependency somePresenterDependency
#InjectMocks
ILoginPresenter mLoginPresenter;
#Before
public void setup() {
MockitoAnnotations.injectMocks(this);
mLoginPresenter.setView(mockView);
}
#Test
public void testLogin() {
mLoginPresenter.logUserIn("", "", "", "");
try {
java.lang.Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Mockito.verify(mockView).dismissProgressDialog();
}
}
If you are doing integration testing and need multiple real objects you could just create an inner/anonymous module for your component that returns the desired object. (instead of trying to mock the module interface).
The code below won't crash when running in JUnit environment. But it crashes when running in the app. I can see error logs in the console, but tests are marked as passed.
#Test
public void test() {
Observable observable = Observable.error(new RuntimeException());
observable.subscribe();
}
So, the question is: how to make it crash in JUnit. Because yeah, if something doesn't work in the app it's a good thing if it doesn't work in the unit tests also :)
And in this example I have direct access to the observable. But in my real tests I don't have that. Real observables are just internal details of classes that being tested. The most thing I can to do is to inject schedulers or something.
So, how to make it crash without having direct access to the observable?
Also, I've just checked this code doesn't crash either:
#Test
public void test() {
Observable observable = Observable.error(new RuntimeException());
observable.subscribe(new Consumer() {
#Override
public void accept(Object o) throws Exception {
throw new RuntimeException();
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
throw new RuntimeException();
}
});
}
According to akarnokd this is RxJava2 specific problem.
"Such usages no longer throw synchronously in 2.x but end up in the plugin handler for errors."
It is possible to check if any errors was thrown with this code
public static List<Throwable> trackPluginErrors() {
final List<Throwable> list = Collections.synchronizedList(new ArrayList<Throwable>());
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
#Override
public void accept(Throwable t) {
list.add(t);
}
});
return list;
}
A small trick I use to tackle this problem, is creating a JUnit4 TestRule class that setups a custom RxJava error handler so it can throw when an unhandled RxJava error occurs:
/**
* A rule that detects RxJava silent errors and reports them to JUnit
(by throwing them).
*/
public class RxErrorRule implements TestRule {
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
Consumer<? super Throwable> previous = RxJavaPlugins.getErrorHandler();
AtomicReference<Throwable> result = setupErrorHandler();
try {
base.evaluate();
} finally {
RxJavaPlugins.setErrorHandler(previous);
}
if (result.get() != null) {
throw result.get();
}
}
};
}
private AtomicReference<Throwable> setupErrorHandler() {
AtomicReference<Throwable> result = new AtomicReference<>();
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) {
result.set(throwable);
}
});
return result;
}
}
And in the unit test:
public class YourRxTest {
#Rule
public RxErrorRule errorRule = new RxErrorRule();
// ...
}
Use TestSubscriber
Observable observable = Observable.error(new RuntimeException());
TestSubscriber testSubscriber = TestSubscriber.create();
observable.subscribe(testSubscriber);
testSubscriber.assertTerminalEvent();
testSubscriber.assertError(RuntimeException.class);
I am struggling to write unit tests for my API implementation in Android. I would like to test the Retrofit functionality but run into concurrency problems, where I do not know how to ensure that the async API calls get executed and finish before I start testing the Android internal database calls.
Here is my test function:
public class postPrintModeTest extends ActivityInstrumentationTestCase2<MainActivity> implements IConstants {
public MainActivity activity;
public postPrintModeTest() {
super(MainActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
activity = getActivity();
String printModeName = "LSD Mode";
int parentId = 4;
Map<String, Object> payload = new HashMap<String, Object>();
payload.put("name", printModeName);
payload.put("parentId", parentId);
APIExec.getInstance().postPrintMode(IConstants.authorization, IConstants.userId, IConstants.deviceUid, payload); // <- this needs to finish before I execute the tests, so I have proper data in the database.
}
#SmallTest
public void testPrintModeCreated() {
DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
assertNotNull("Print Mode does not exist", printMode);
}
#SmallTest
public void testPrintModeName() {
DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
if(printMode != null)
{
assertTrue("Print Mode name is not correct", printMode.getName().equals("LSD Mode"));
}
}
}
and here is the async method in question:
public void postPrintMode(String authorization, final int userid, String deviceuid, final Map payload){
api.postPrintMode(authorization, userid, deviceuid, payload, new Callback<PrintMode>() {
#Override
public void success(PrintMode printMode, Response response) {
if (printMode.get_id() != 0) {
dbOps.writePrintMode(userid, printMode);
bus.getBus().post(new EVTNewPrintMode(printMode));
}
}
#Override
public void failure(RetrofitError retrofitError) {
retrofitError.printStackTrace();
APIUtils.showAPIResponseBody(retrofitError);
}
});
}
is there a more elegant way to do an assert throws exception in Android then this?
public void testGetNonExistingKey() {
try {
alarm.getValue("NotExistingValue");
fail( );
} catch (ElementNotFoundException e) {
}
}
Something like this does not work?!
#Test(expected=ElementNotFoundException .class)
Thanks, Mark
Are you using a junit4 test runner? The #Test annotation won't work if you're running a junit3 test runner. Check the version that you're using.
Secondly, the recommended way to check for exceptions in your code is to use a Rule (introduced in junit 4.7).
#Rule
public ExpectedException exception = ExpectedException.none();
#Test
public void throwsIllegalArgumentExceptionIfIconIsNull() {
// do something
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Icon is null, not a file, or doesn't exist.");
new DigitalAssetManager(null, null);
}
You can continue to use the #Test(expected=IOException.class), but the above has the advantage that if an exception is thrown before the exception.expect is called, then the test will fail.
I did something very similar to hopia's answer with a couple of improvements. I made it return the exception object so that you can check its message or any other properties, and I declared a Testable interface to replace Runnable because Runnable doesn't let your code under test throw checked exceptions.
public interface Testable {
public void run() throws Exception;
}
public <T extends Exception> T assertThrows(
final Class<T> expected,
final Testable codeUnderTest) throws Exception {
T result = null;
try {
codeUnderTest.run();
fail("Expecting exception but none was thrown.");
} catch(final Exception actual) {
if (expected.isInstance(actual)) {
result = expected.cast(actual);
}
else {
throw actual;
}
}
return result;
}
Here's an example of calling it.
InvalidWordException ex = assertThrows(
InvalidWordException.class,
new Testable() {
#Override
public void run() throws Exception {
model.makeWord("FORG", player2);
}
});
assertEquals(
"message",
"FORG is not in the dictionary.",
ex.getMessage());
If you're using Kotlin, you can take advantage of reified types to avoid passing the Exception subclass as an argument:
inline fun <reified T : Exception> assertThrows(runnable: () -> Any?) {
try {
runnable.invoke()
} catch (e: Throwable) {
if (e is T) {
return
}
Assert.fail("expected ${T::class.qualifiedName} but caught " +
"${e::class.qualifiedName} instead")
}
Assert.fail("expected ${T::class.qualifiedName}")
}
#Test
fun exampleTest() {
val a = arrayOf(1, 2, 3)
assertThrows<IndexOutOfBoundsException> {
a[5]
}
}
This is how I do it. I create a static method called assertThrowsException that takes in as arguments an expected exception class and a Runnable which contains the code under test.
import junit.framework.Assert;
public SpecialAsserts {
public void assertThrowsException(final Class<? extends Exception> expected, final Runnable codeUnderTest) {
try {
codeUnderTest.run();
Assert.fail("Expecting exception but none was thrown.");
} catch(final Throwable result) {
if (!expected.isInstance(result)) {
Assert.fail("Exception was thrown was unexpected.");
}
}
}
}
This is the sample code to use the special assert in your test class (that extends AndroidTestCase or one of its derivatives):
public void testShouldThrowInvalidParameterException() {
SpecialAsserts.assertThrowsException(InvalidParameterException.class, new Runnable() {
public void run() {
callFuncThatShouldThrow();
}
});
}
Yes, there's a lot of work, but it's better than porting junit4 to android.
With junit3 the following might help.
public static void assertThrows(Class<? extends Throwable> expected,
Runnable runnable) {
try {
runnable.run();
} catch (Throwable t) {
if (!expected.isInstance(t)) {
Assert.fail("Unexpected Throwable thrown.");
}
return;
}
Assert.fail("Expecting thrown Throwable but none thrown.");
}
public static void assertNoThrow(Runnable runnable) {
try {
runnable.run();
} catch (Throwable t) {
Assert.fail("Throwable was unexpectedly thrown.");
}
}