Android Tests: Stubbing out Retrofit with Mockito - java

In my MainActivity I have a method called getAPI that returns an OTBServiceWrapper. This is used to setup retrofit for calling to an API.
In my MainActivityTest file I am trying to stub out the new OTBService().getService() call that the getApi method is making so I can return a MockedOTBService which changes the client to a custom one that return json.
As is, the current implementation will it the MockedOTBService if I had to place a logger within MockedOTBService but also falls through and calls the real api, which is not want I want in a test.
I am trying to stub the Retrofit API calls using Mockito and return json. I cant seem to understand why the stub is being called yet is not stubbing the method in question.
Notes:
I am using ActivityInstrumentationTestCase2
I am only running one test
If I add a verify(mockedOTBService, atLeastOnce()).getService(); is says it was never called.
If I change the when...thenReturn to use a mMainActivity = spy(getActivity()) there is not change and the real API is called.
Logcat Output
Logger﹕ MockedOTBService was called // Mock is called
Logger﹕ Real OTBService was called // Real API is called
Logger﹕ MainActivity getAPI method class is "$Proxy1" // Mock is shown in MainActivity
Logger﹕ RealAPIResponse JSON Parsed ID: 266 // Real API response returned
Real Flow
MainActivity.onCreate() > OTBService.getService() > OTBServiceWrapper.createSearch(...)
Trying to Achieve within Tests
MainActivity.onCreate() > MockedOTBService.getService() > OTBServiceWrapper.createSearch(...)
MainActivity.java
public class MainActivity extends Activity {
private OTBServiceWrapper serviceWrapper;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getApi().createSearch(...)
}
public OTBServiceWrapper getApi() {
return new OTBService().getService();
}
}
OTBService.java
public class OTBService {
public OTBServiceWrapper getService() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constants.API_URL)
.build();
return restAdapter.create(OTBServiceWrapper.class);
}
}
OTBServiceWrapper.java
public interface OTBServiceWrapper {
#POST(Constants.API_SEARCHES_POST_URL)
void createSearch(#Body Request request, Callback<Request.Response> callback);
}
MainActivityTest.java
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
private OTBService mMockedOTBService;
private MainActivity mMainActivity;
private View mSearchButton;
public MainActivityTest() { super(MainActivity.class); }
#Override
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(true);
System.setProperty("dexmaker.dexcache", getInstrumentation().getTargetContext().getCacheDir().getPath());
mMockedOTBService = mock(OTBService.class);
when(mMockedOTBService.getService()).thenReturn(new MockedOTBService(getInstrumentation().getContext()).getService());
mMainActivity = getActivity();
mSearchButton = mMainActivity.findViewById(R.id.AbSearchButton);
mYourHolidayButton = mMainActivity.findViewById(R.id.AbYourHolidayButton);
}
public void testButtonActions() {
TouchUtils.clickView(this, mSearchButton);
...
}
}
MockedOTBService.java
public class MockedOTBService {
private Context context;
public MockedOTBService(Context context) { this.context = context; }
public OTBServiceWrapper getService() {
RestAdapter restAdapter;
restAdapter = new RestAdapter.Builder()
.setClient(new LocalJsonClient(context))
.setEndpoint(Constants.API_TEST_URL)
.build();
return restAdapter.create(OTBServiceWrapper.class);
}
}
LocalJsonClient.java
#SuppressLint("DefaultLocale")
public class LocalJsonClient implements Client { ... }
build.gradle
dependencies {
androidTestCompile 'com.google.dexmaker:dexmaker:1.0'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.0'
}

Remove the need for mocking your request by allowing the Activity to set the service.
In your MainActivity create a class variable and a class setter for the service. It needs to be a at the class scope to prevent the OnCreate method being called before you have set the service to what you want it to be. Also create an instance getter which sets the service if you have not already.
In your test before you call getActivity() set the service to be your mock service. (Maybe think about moving this out to a support object).
MainActivity.java
public class MainActivity extends Activity {
private static OTBServiceWrapper serviceWrapper;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getServiceWrapper.createSearch(...)
}
public OTBServiceWrapper getServiceWrapper() {
if (serviceWrapper == null) {
MainActivity.setServiceWrapper(new OTBService().getService());
}
return serviceWrapper;
}
public static void setServiceWrapper(OTBServiceWrapper serviceWrapper) {
MainActivity.serviceWrapper = serviceWrapper;
}
}
MainActivityTest.java
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
private MainActivity mMainActivity;
public MainActivityTest() { super(MainActivity.class); }
#Override
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(true);
MainActivity.setServiceWrapper(
new MockedOTBService(getInstrumentation().getContext()).getService()
);
mMainActivity = getActivity();
}
}

Related

How to use Retrofit in android studio? and what is my problem?

i made interface like this in "TestService.java"
public interface TestService {
#GET("/api/users/2")
Call<String> getTest();
}
and
"RetrofitClient.java"
public class RetrofitClient {
private static Retrofit instance;
public static Retrofit getInstance() {
if(instance == null)
instance = new Retrofit.Builder()
.baseUrl("https://reqres.in/")
.build();
return instance;
}
}
in "MainActivity.java"
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofitClient = RetrofitClient.getInstance();
TestService testService = retrofitClient.create(TestService.class);
Call<String> repos = testService.getTest(); //problem
}
}
i'm first at android java, and i don't know how to use Retrofit..
what is the problem and how to print response? ( i need also header information)
You need to asynchronously call it on the main thread.
Call<ResponseBody> repos = testService.getTest();
repos.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
//response
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
//error
}
});
There are plenty of resources online, to know more you can go through the following articles.
vogella
android.jlelse

Unable to instantiate ViewModel in Android

I am trying to achieve MVVM design pattern in my application.I have created viewmodel and repository class but when I am trying to instantiate viewmodel in my MainActivity its showing error red line below MainActivity at the time of instantiation in below line.
pdfViewModel = new ViewModelProvider(MainActivity.this).get(PdfViewModel.class);
Below is my code:
MainActivity.java
public class MainActivity extends AppCompatActivity {
PdfViewModel pdfViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pdfViewModel = new ViewModelProvider(MainActivity.this).get(PdfViewModel.class);
}
}
PdfViewModel.java
public class PdfViewModel extends AndroidViewModel {
private PdfRepository pdfRepository;
public PdfViewModel(#NonNull Application application) {
super(application);
pdfRepository = new PdfRepository(application);
}
public LiveData<List<Pdfs>> getAllPdfs(){
return pdfRepository.getMutableLiveData();
}
}
PdfRepository.java
public class PdfRepository {
private ArrayList<Pdfs> list = new ArrayList<>();
private MutableLiveData<List<Pdfs>> mutableLiveData = new MutableLiveData<>();
private Application application;
public PdfRepository(Application application){
this.application = application;
}
public MutableLiveData<List<Pdfs>> getMutableLiveData(){
SharedPreferences preferences = application.getSharedPreferences("Credentials", Context.MODE_PRIVATE);
String email = preferences.getString("email",null);
Retrofit retrofit = RetrofitClient.getInstance();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<Pdfs>> call = apiService.getFiles(email);
call.enqueue(new Callback<List<Pdfs>>() {
#Override
public void onResponse(Call<List<Pdfs>> call, Response<List<Pdfs>> response) {
if(response.body() != null){
list = (ArrayList<Pdfs>) response.body();
mutableLiveData.setValue(list);
}
}
#Override
public void onFailure(Call<List<Pdfs>> call, Throwable t) {
TastyToast.makeText(application,t.getMessage(),TastyToast.LENGTH_SHORT,TastyToast.ERROR).show();
}
});
return mutableLiveData;
}
}
What needs to be corrected in the above code?
Your code is trying to create a new instance of the class ViewModelProvider (with the new keyword) and that's not the right way to instantiate a ViewModel.
On MainActivity, instead of:
pdfViewModel = new ViewModelProvider(MainActivity.this).get(PdfViewModel.class);
try:
pdfViewModel = ViewModelProviders.of(this).get(PdfViewModel.class);
Notice the right class is ViewModelProviders (with an "s" at the end) and you need to call the static method of instead of creating a new instance of it with new. If you can't import that class, make sure you have the dependency 'androidx.lifecycle:lifecycle-extensions:2.2.0' added to app/build.gradle.
To make your code even clearer, I'd suggest learning about the Kotlin KTX method viewModels, as described here. You'd need to use Kotlin for that though.

Getting data from async task onpostexecute using interface

Hi I am trying to get an arraylist of data from a an async task class to another main class:
I was following the answer below but I am a little lost:
How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?
So I have my class that extends async task and calls to the database to get my object array:
public class GetVideoInfoFromDataBase extends AsyncTask {
// Paginated list of results for song database scan
static PaginatedScanList<AlarmDynamoMappingAdapter> results;
// The DynamoDB object mapper for accessing DynamoDB.
private final DynamoDBMapper mapper;
public interface AlarmsDataBaseAsyncResponse {
void processFinish(PaginatedScanList<AlarmDynamoMappingAdapter> output);
}
public AlarmsDataBaseAsyncResponse delegate = null;
public GetVideoInfoFromDataBase(AlarmsDataBaseAsyncResponse delegate){
mapper = AWSMobileClient.defaultMobileClient().getDynamoDBMapper();
this.delegate = delegate;
}
#Override
protected Object doInBackground(Object[] params) {
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
results = mapper.scan(AlarmDynamoMappingAdapter.class, scanExpression);
return results;
}
#Override
public void onPostExecute(Object obj) {
delegate.processFinish(results);
}
}
There are no errors but I think I have done something incorrectly in it causing my error.
So in my main activity to call the results I have:
GetVideoInfoFromDataBase asyncTask =new GetVideoInfoFromDataBase(new GetVideoInfoFromDataBase.AlarmsDataBaseAsyncResponse(){
#Override
public void processFinish(PaginatedScanList<AlarmDynamoMappingAdapter> output) {
}
}).execute();
I have two problems here
I am getting the error:
"incompatible types: AsyncTask cannot be converted to GetVideoInfoFromDataBase"
In the mainactivity where i have:
`new GetVideoInfoFromDataBase(new GetVideoInfoFromDataBase.AlarmsDataBaseAsyncResponse()`
it wants me to cast it like this:
(GetVideoInfoFromDataBase) new GetVideoInfoFromDataBase(new GetVideoInfoFromDataBase.AlarmsDataBaseAsyncResponse()
That doesn't seem right but I thought i would check.
I am not sure how to return the result when overriding the onprocessfinished.
Thanks in advance for your help
First create an Interface
public interface AsyncInterface {
void response(String response);
}
Assign it in the asynctask class as below :-
Context context;
Private AsyncInterface asyncInterface;
AsyncClassConstructor(Context context){
this.context = context;
this.asyncInterface = (AsyncInterface) context;
}
Then inside onPostExecute method of asynctask class :-
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
asyncInterface.response(s);
}
Then implement this interface in your activity :-
class MainActivity extends AppCompatActivity implements AsyncInterface {
and then import the method of asyncInterface
#Override
public void response(String response) {
//Here you get your response
Log.e(TAG, response);
}
Modify Constructor of class.
Need default constructor. By the way, create method to set Interface.
public void setInterface(AlarmsDataBaseAsyncResponse delegate){
this.delegate = delegate;}
In MainActivity, push your logic in:
object.setInterface(new AlarmsDataBaseAsyncResponse(){
#Override
public void processFinish(PaginatedScanList<AlarmDynamoMappingAdapter> output) {
//your logic
}
});

Android testing - Robolectric + Mockito + Retrofit getting Wanted but not invoked error

Trying to use Robolectric and Mockito to test my Retrofit calls in my Android app but I am getting the following error:
Wanted but not invoked: mockApi.register(
,
);
-> at ServiceTest.testAPI(ServiceTest.java:58) Actually, there were zero interactions with this mock.
The RetroFit API call is defined in an interface as follows:
#FormUrlEncoded
#POST("/register")
void register(
#FieldMap Map<String, String> registrationParams,
Callback<JsonObject> response) ;
My test class is as follows:
#Config(constants = BuildConfig.class)
#RunWith(TestRunner.class)
public class SharedServiceTest {
private RegistrationActivity activity;
#Mock
private SharedService mockApi;
#Captor
private ArgumentCaptor<Callback<JsonObject>> cb;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ActivityController<RegistrationActivity> controller = Robolectric.buildActivity(RegistrationActivity.class);
activity = controller.get();
controller.create();
}
#Test
public void testAPI() throws Exception {
activity.populateFields();
activity.validateFields();
activity.register("");
Mockito.verify(mockApi).register(Mockito.anyMap(), cb.capture());
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("sessionToken", Mockito.anyString());
jsonObject.addProperty("userId", Mockito.anyString());
cb.getValue().success(jsonObject, null);
Assert.assertTrue(ShadowToast.getTextOfLatestToast().contains("Registration completed"));
}
}
The method in my RegistrationActivity that uses the API is as follows:
public void register(){
MyApplication.getInstance().getSharedService().register(mRegistrationParams, new Callback<JsonObject>() {
#Override
public void success(JsonObject jsonObject, retrofit.client.Response response) {
Toast.makeText(RegistrationActivity.this, "Registration completed", Toast.LENGTH_LONG).show();
}
#Override
public void failure(RetrofitError error) {
Toast.makeText(RegistrationActivity.this, RetrofitUtils.getErrorMessage(error), Toast.LENGTH_LONG).show();
}
});
}
The real Retrofit service comes from my own Application class which I have mocked in my test folder for robolectric to use:
public class TestMyApplication extends MyApplication
implements TestLifecycleApplication {
#Override
public void onCreate() {
super.onCreate();
}
#Override public void beforeTest(Method method) {
}
#Override public void prepareTest(Object test) {
}
#Override public void afterTest(Method method) {
}
#Override public CPSharedService getCPSharedService() {
return Mockito.mock(SharedService.class);
}
}
I have searched over the other questions on SO that have this error but none of them match what I am trying to do here or provide a solution to my issue so I am just wondering what I am doing wrong?
The mocked instance of SharedService in your TestMyApplication is not the same you declared your test class.
The Mockito.verify(mockApi).register(Mockito.anyMap(), cb.capture()); is failing because the instance referred by mockApi field is actually never called.
Another problem is that the getter in TestMyApplication always returns a new mock for each invokation:
#Override public CPSharedService getCPSharedService() {
return Mockito.mock(SharedService.class); //this creates a new "mocked" instance
}
Your scenario is not 100% clear to me, but it would be better if you could let your test set the instance of the mockApi field in your TestMyApplication instance:
public class TestMyApplication extends MyApplication
implements TestLifecycleApplication {
private SharedService sharedService;
#Override
public void onCreate() {
super.onCreate();
}
#Override public void beforeTest(Method method) {
}
#Override public void prepareTest(Object test) {
}
#Override public void afterTest(Method method) {
}
#Override public CPSharedService getCPSharedService() {
return this.sharedService;
}
public void setCPSharedService(SharedService sharedService) {
// store your mock
this.sharedService = sharedService;
}
}
and in your test class:
#Test
public void testAPI() throws Exception {
// configure you TestMyApplication
assertTrue(MyApplication.getInstance() instanceof TestMyApplication);
TestMyApplication testMyApp = (TestMyApplication) MyApplication.getInstance();
testMyApp.setCPSharedService(this.mockApi);
activity.populateFields();
activity.validateFields();
activity.register("");
Mockito.verify(this.mockApi).register(Mockito.anyMap(), cb.capture());
...
}

Retrofit override method failure

I am new to android programming and I am trying to connect to server with retrofit and get some data. I made a little example just to check if it would return some data. First there is a problem that I don't know if I even wrote the code to do what I want and second I get the errors:
"Error:(64, 52) error: is not abstract and does not override abstract method failure(RetrofitError) in Callback"
and 2 errors " Error:(67, 13) error: method does not override or implement a method from a supertype"
Here is my code
public class MainActivity extends ListActivity{
public static final String ENDPOINT = "http://tinoba.hostzi.com";
List<Jelovnik> jelovnik;
Button gumb;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gumb = (Button)findViewById(R.id.gumb);
}
public void stisni(View view) {
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(ENDPOINT)
.build();
JelovnikAPI api = adapter.create(JelovnikAPI.class);
api.getFeed(new Callback<List<Jelovnik>>() {
#Override
public void onResponse(Response<List<Jelovnik>> response, Retrofit retrofit) {
jelovnik = response.body();
gumb.setText(jelovnik.get(0).getIme().toString());
}
#Override
public void onFailure(Throwable throwable) {
}
});
}
}
and my retrofit interface
public interface JelovnikAPI {
#GET("/read.php")
public void getFeed(Callback<List<Jelovnik>> response);
}
The version of Callback you are using is from Retrofit 2 and you are still using Retrofit 1.x. Callback has two methods, failure and success. Your callback should look like
new Callback<List<Jelovnik>>() {
#Override
success(List<Jelovnik> t, Response response) {
}
#Override
public void failure(RetrofitError error) {
}
});
Replace Throwable with RetrofitError:
#Override
public void onFailure(RetrofitError retrofitError) {
}

Categories