Mockito Android Unit testing - android

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).

Related

How to verify void method with the Mockito

I'm writing some unitTest for a class that its name is NetworkRequest.
In this class, I have a function that its name is run.
public void run(NetworkResponseListener listener) {
if (listener == null)
return;
this.mListener = listener;
CheckInternetAccess.checkInternetAccess(manager, new NetworkStateListener() {
#Override
public void onInternetResponse(int internetCode) {
if (internetCode == ErrorCodes.CODE_HAS_INTERNET_ACCESS) {
runRequest();
} else {
mListener.onNetworkResponse(null);
}
}
});
}
I want to create a test when the input listener(NetworkResponseListener)
be null.
I wrote something like below:
public class NetworkRequestTest {
private ReceivedRequest receivedRequest = Mockito.mock(ReceivedRequest.class);
private ConnectivityManager connectivityManager = Mockito.mock(ConnectivityManager.class);
private NetworkStateListener networkStateListener = Mockito.mock(NetworkStateListener.class);
#Test
public void checkInternetAccessShouldNotCallWithNullListener() {
NetworkRequest networkRequest = new NetworkRequest(receivedRequest, connectivityManager);//SUT
networkRequest.run(null);
Mockito.verify(CheckInternetAccess.checkInternetAccess(connectivityManager, networkStateListener), Mockito.never());
}
}
I want to check verification of CheckInternetAccess.checkInternetAccess
But I got an error that says:
Wrong 1st argument type. Found: 'void', required: 'java.lang.Void'
Try using Mockito.verifyZeroInteractions(...) on your mock objects.

What is the product flavor for androidTest

The first time, I started to write test cases for Android app, I have searched a lot but could not find the full-phased proper way
Like I am testing Instrumentation Test cases in "androidTest" folder
Below is code for LoginTestCases
#Test
public void loginUserAccount() {
NetworkHelper.MOCK_URL =
String userName = "abacus#hotmail.com";
String password = "abacus#112";
boolean keepMeLoggedInChecked = false;
boolean touchIdEnabled = false;
boolean optIn = false;
mAuthenticationService.logIn(userName, password, keepMeLoggedInChecked, touchIdEnabled, optIn).done(new DoneCallback<LogInResponse>() {
#Override
public void onDone(final LogInResponse result) {
Assert.assertEquals(true,true);
}
}).fail(new FailCallback<Throwable>() {
#Override
public void onFail(Throwable result) {
Assert.assertEquals(true,false);
}
});
}
For testing I need to change MOCK_URL value to "/" so I can implement MockWebServer and choose what response I want, I have used static variable MOCK_URL like below code
public class NetworkHelper {
private final OkHttpClient mOkHttpClient;
public static HttpUrl MOCK_URL = null;
public NetworkHelper(OkHttpClient httpClient) {
mOkHttpClient = httpClient;
}
public Retrofit initialize(String baseUrl) {
if(MOCK_URL!=null) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(MOCK_URL)
.addConverterFactory(JacksonConverterFactory.create())
.client(mOkHttpClient)
.build();
return retrofit;
I know that static is not the proper way if anybody knows another way then please give me the solution.

android MVP - How should the network layer be called , from model?

In MVP android i believe the network layer (retrofit , volley etc) should NOT be apart of the model. But I need a firm example on how to construct the model then. Should the model be a singleton that the network layer simply creates when api call completes ?
Lets take a look at a presenter i have for my one of my activities:
public class MainActivityPresenter implements IMainPresenterContract, Callback {
IMainActivityViewContract view;//todo set up a weak reference to View to avoid leakage
NewsService interactor;
public MainActivityPresenter(IMainActivityViewContract view, NewsService interactor) {
this.view = view;
this.interactor = interactor;
}
public void loadResource() {
interactor.loadResource();
}
public void onRequestComplete(final NewsEntities newsEntities) {
view.dataSetUpdated(newsEntities.getResults());
}
#Override
public void onResult(final NewsEntities newsEntities) {
onRequestComplete(newsEntities);
}
public void goToDetailsActivity(Result result) {
view.goToDetailsActivity(result);
}
}
So my question is about the NewsService interactor parameter i am passing into the constructor. I was assuming this should be model data and not a networking service. But what should it look like then ? Currently mine looks like this:
public class NewsService implements INewsServiceContract {
private Gson gson;
private Callback mCallback;
public NewsService() {
configureGson();
}
private static String readStream(InputStream in) {
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
String nextLine;
while ((nextLine = reader.readLine()) != null) {
sb.append(nextLine);
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
public void setCallBack(Callback cb) {
mCallback = cb; // or we can set up event bus
}
private void configureGson() {
GsonBuilder builder = new GsonBuilder();
builder.excludeFieldsWithoutExposeAnnotation();
gson = builder.create();
}
#Override
public void loadResource() {
//Todo could use a loader instead help with the config change or a headless fragment
new AsyncTask<String, String, String>() {
#Override
protected String doInBackground(String... params) {
String readStream = "";
HttpURLConnection con = null;
try {
URL url = new URL("https://api.myjson.com/bins/nl6jh");
con = (HttpURLConnection) url.openConnection();
readStream = readStream(con.getInputStream());
} catch (Exception e) {
e.printStackTrace();
}
finally {
if(con!=null)
con.disconnect();
}
return readStream;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
NewsService.this.onRequestComplete(result);
}
}.execute();
}
private void onRequestComplete(String data) {
data = data.replaceAll("\"multimedia\":\"\"", "\"multimedia\":[]");
news.hotels.com.sample.Model.NewsEntities newsEntities = gson.fromJson(data, NewsEntities.class);
mCallback.onResult(newsEntities);
}
}
so as you can see the NewsService in this case is doing the network calls. I think i should not have passed this into the presenter. But how can the model be constructed then ? who calls the NewsService ?
UPDATE: THIS QUESTION WAS a long time ago, everyone please use clean architecture approach, and let your presenter know nothing about the network layer.
the network calls need to be in Model layer and should be triggered from the presenter. but its the Model layer who decides where to et the data and its hidden from the Presenter layer.
I myself use an interactor class to do this that is in model layer and a presenter will use this interactor to get the data and the interactor will get the data from DB or Server regarding to the situation.
look at this sample project in my repo:
https://gitlab.com/amirziarati/Echarge
I used Dagger to do DI that may confuse you. just look at packaging and how i seperate concerns between layers.
UPDATE: I used presenter to sync data from server and DB which is WRONG. the presenter should know nothing about this proccess. I didnt recognize this problem that time.

Test running failed: Instrumentation run failed due to 'android.os.NetworkOnMainThreadException'

I've started working with Espresso UI tests. I prepare custom MockTestRunner, MockApplication for initialization Dagger components and I've defined mock modules too. It looks like that:
public class MockTestRunner extends AndroidJUnitRunner {
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return super.newApplication(cl, MockMyApplication.class.getName(), context);
} }
MyApp is extended by
public class MockQrApplication extends MyApp {
private MockWebServer mockWebServer;
protected void initComponent() {
mockWebServer = new MockWebServer();
component = DaggerMyAppComponent
.builder()
.myAppModule(new MyAppModule(this))
.busModule(new BusModule())
.apiModule(new MockApiModule(mockWebServer))
.facebookModule(new FacebookModule())
.dataManagerModule(new DataManagerModule())
.greenDaoModule(new GreenDaoModule())
.trackModule(new TrackModule(this))
.build();
component.inject(this);
}
}
I added testInstrumentationRunner into gradle
defaultConfig {
....
multiDexEnabled true
testInstrumentationRunner "a.b.c.MockTestRunner"
}
I want run login tests in my LoginActivity
#RunWith(AndroidJUnit4.class)
#LargeTest
public class LoginActivityTest {
protected Solo solo;
#Rule
public ActivityTestRule<LoginActivity> activityTestRule = new ActivityTestRule(LoginActivity.class);
#Before
public void setUp() throws Exception {
initVariables();
}
protected void initVariables() {
solo = new Solo(InstrumentationRegistry.getInstrumentation(), activityTestRule.getActivity());
}
#Test
public void testLayout() {
solo.waitForFragmentByTag(LoginFragment.TAG, 1000);
onView(withId(R.id.email_input)).perform(clearText(), typeText("developer#appppp.com"));
onView(withId(R.id.pass_input)).perform(clearText(), typeText("qqqqqqqq"));
onView(withId(R.id.login_button)).perform(click());
solo.waitForDialogToOpen();
}
}
When I want to run my tests I got:
Client not ready yet..
Started running tests
Test running failed: Instrumentation run failed due to 'android.os.NetworkOnMainThreadException'
Empty test suite.
[Edit]
This is MockApiModule which extends ApiModule class
public class MockApiModule extends ApiModule {
private MockWebServer mockWebServer;
public MockApiModule(MockWebServer mockWebServer) {
this.mockWebServer = mockWebServer;
}
#Override
public OkHttpClient provideOkHttpClient(DataManager dataManager) {
return new OkHttpClient.Builder()
.build();
}
#Override
public Retrofit provideRetrofit(OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(mockWebServer.url("/")) // throw NetworkOnMainThreadException
.addConverterFactory(NullOnEmptyConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(okHttpClient)
.build();
return retrofit;
}
#Override
public ApiService provideApiService(Retrofit retrofit) {
return retrofit.create(ApiService.class);
}
#Override
public ApiClient provideApiManager(Application application, ApiService apiService, DataManager dataManager) {
return new MockApiClient(application, apiService, dataManager, mockWebServer);
}
}
API login request looks like that:
apiService.postLoginUser(userLoginModel).enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
if (response.isSuccess()) {
LoginResponse loginResponse = null;
try {
loginResponse = gson.fromJson(MockResponse.getResourceAsString(this, "login.json"), LoginResponse.class);
} catch (IOException e) {
e.printStackTrace();
}
callback.onLoginSuccess(loginResponse);
} else {
callback.onLoginFail(response.errorBody().toString());
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
callback.onLoginFail(t.getMessage());
}
});
It works if I change MockMyApplication into MyApp class of application in MockTestRunner
I get stracktrace
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1285)
at java.net.InetAddress.lookupHostByName(InetAddress.java:431)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252)
at java.net.InetAddress.getByName(InetAddress.java:305)
at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.java:303)
at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.java:293)
at okhttp3.mockwebserver.MockWebServer.maybeStart(MockWebServer.java:143)
at okhttp3.mockwebserver.MockWebServer.getHostName(MockWebServer.java:172)
at okhttp3.mockwebserver.MockWebServer.url(MockWebServer.java:198)
at com.mooduplabs.qrcontacts.modules.MockApiModule.provideRetrofit(MockApiModule.java:38)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideRetrofitFactory.get(ApiModule_ProvideRetrofitFactory.java:23)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideRetrofitFactory.get(ApiModule_ProvideRetrofitFactory.java:9)
at dagger.internal.ScopedProvider.get(ScopedProvider.java:46)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideApiServiceFactory.get(ApiModule_ProvideApiServiceFactory.java:23)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideApiServiceFactory.get(ApiModule_ProvideApiServiceFactory.java:9)
at dagger.internal.ScopedProvider.get(ScopedProvider.java:46)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideApiManagerFactory.get(ApiModule_ProvideApiManagerFactory.java:31)
at com.mooduplabs.qrcontacts.modules.ApiModule_ProvideApiManagerFactory.get(ApiModule_ProvideApiManagerFactory.java:11)
at dagger.internal.ScopedProvider.get(ScopedProvider.java:46)
at com.mooduplabs.qrcontacts.activities.BaseActivity_MembersInjector.injectMembers(BaseActivity_MembersInjector.java:44)
at com.mooduplabs.qrcontacts.activities.BaseActivity_MembersInjector.injectMembers(BaseActivity_MembersInjector.java:13)
at com.mooduplabs.qrcontacts.components.DaggerQrContactsAppComponent.inject(DaggerQrContactsAppComponent.java:91)
at com.mooduplabs.qrcontacts.activities.BaseActivity.init(BaseActivity.java:74)
at com.mooduplabs.qrcontacts.activities.BaseActivity.onCreate(BaseActivity.java:64)
at android.app.Activity.performCreate(Activity.java:6367)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
at android.support.test.runner.MonitoringInstrumentation.callActivityOnCreate(MonitoringInstrumentation.java:532)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2404)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2511)
at android.app.ActivityThread.access$900(ActivityThread.java:165)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1375)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5621)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)
Ran into similar issue, just avoid starting your activity right away during testing. You can achieve this by adjusting your test rule like the snippet below,
#Rule
// Do not launch the activity right away
public ActivityTestRule<LoginActivity> activityTestRule = new ActivityTestRule(LoginActivity.class, true, false);
and then starting the mock web server by yourself during test setup. It is basically happening because mockWebServer.url("/") tries to start the mock server for you in case it has not already started. You need to do it first if you don't want to do it later on during test execution (NetworkOnMainThreadException case).

NPE when getting Robolectric ShadowApplication with Volley and Dagger

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.

Categories

Resources