How to mock an object using Mockito in Android - android

I am trying to write a test for a function. Which looks like this
class ClassManager {
fun testFunction() {
ApplicationClass.getName()
}
}
class ApplicationClass: Application {
private appPrefs: SharedPreferences
public void onCreate() {
appPrefs = AppPreferences(this).getSharedPreferencesInstance();
}
public static String getName() {
return appPrefs.getString("test_name", ""); -> throws null pointer for appPrefs object
}
}
Now, whenever I execute
fun testFunctionTest() {
val sut = ClassManager()
Mockito.`when`(ApplicationClass.getName()).thenReturn("test_string")
sut.testFunction()
}
Throws Null pointer exception for appPrefs object in getName() method.
The question is how should I mock appPrefs object? I do not even have direct access to it.

Related

One method get respond from WebServer and other method need answer of that method to return something , and unfortunately I got null

In MainActivityViewModel class i have one Getter method that returns an instance of CurrentWeather (pojo class) and this method needs response from OnResponse method but I get null for first time.
The first methods invoke from MainActivity, viewModel is not null but the currentWeather instance is.
MainActivityViewModel viewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
currentWeather = viewModel.getCurrentWeather();
I don't know if I can ask to wait for a moment before return currentWeather in first method or not.
public class MainActivityViewModel extends ViewModel implements Callback<ResponseBody> {
private CurrentWeather currentWeather;
public CurrentWeather getCurrentWeather() {
if (currentWeather == null) {
createCurrentWeather("London");
}
return currentWeather;
}
public void createCurrentWeather(String city) {
RetrofitApiManager.getInstance().getCurrentWeatherApi(this, city);
}
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
ResponseBody body = response.body();
try {
String serverResponde = body.string();
Timber.e(serverResponde);
Gson gson = new Gson();
currentWeather = gson.fromJson(serverResponde, CurrentWeather.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
}
It's because it takes a while before a response is returned.
Usually, you need a LiveData object to get results from background tasks.
In your MainActivityViewModel, add the following:
private MutableLiveData currentWeatherData = new MutableLiveData<CurrentWeather>();
public LiveData<CurrentWeather> getCurrentWeatherData() {
return currentWeatherData;
}
When you get response, update your LiveData
currentWeather = gson.fromJson(serverResponde, CurrentWeather.class);
currentWeatherData.postValue(currentWeather);
In your activity, you need to observe this LiveData.
viewModel.getCurrentWeatherData().observe(this, new Observer<CurrentWeather>() {
#Override
public void onChanged(CurrentWeather c) {
// Do whatever you want with c.
}
});

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.

Error while trying to cache a HashSet using Android Room Library

I'm willing to try the new Room Library from Android and I met the below error:
Error:(19, 29) error: Cannot figure out how to save this field into
database. You can consider adding a type converter for it.
This error refers to the following class member:
private HashSet<String> fruits;
I have the following class:
#Entity(tableName = "SchoolLunches")
public class SchoolLunch {
#PrimaryKey(autoGenerate = true)
private int lunchId;
private boolean isFresh;
private boolean containsMeat;
private HashSet<String> fruits;
public int getLunchId() {
return lunchId;
}
public void setLunchId(int lunchId) {
this.lunchId = lunchId;
}
public boolean isFresh() {
return isFresh;
}
public void setFresh(boolean fresh) {
isFresh = fresh;
}
public boolean isContainsMeat() {
return containsMeat;
}
public void setContainsMeat(boolean containsMeat) {
this.containsMeat = containsMeat;
}
public HashSet<String> getFruits() {
return fruits;
}
public void setFruits(HashSet<String> fruits) {
this.fruits = fruits;
}
Also, there is a relative DAO class:
#Dao
public interface SchoolLunchDAO {
#Query("SELECT * FROM SchoolLunches")
List<SchoolLunch> getAll();
#Insert
void insertAll(SchoolLunch... schoolLunches);
#Query("DELETE FROM SchoolLunches")
void deleteAll();
}
Since I'm trying to be a very good developer, I wrote a unit test as follows:
#Test
public void singleEntityTest() {
HashSet<String> fruitSet = new HashSet<>();
fruitSet.add("Apple");
fruitSet.add("Orange");
SchoolLunch schoolLunch = new SchoolLunch();
schoolLunch.setContainsMeat(false);
schoolLunch.setFresh(true);
schoolLunch.setFruits(fruitSet);
schoolLunchDAO.insertAll(schoolLunch);
List<SchoolLunch> schoolLunches = schoolLunchDAO.getAll();
assertEquals(schoolLunches.size(), 1);
SchoolLunch extractedSchoolLunch = schoolLunches.get(0);
assertEquals(false, extractedSchoolLunch.isContainsMeat());
assertEquals(true, extractedSchoolLunch.isFresh());
assertEquals(2, extractedSchoolLunch.getFruits().size());
}
What should I do here?
What should I do here?
You could create a type converter, as suggested by the error message. Room does not know how to persist a HashSet<String>, or a Restaurant, or other arbitrary objects.
Step #1: Decide what basic type you want to convert your HashSet<String> into (e.g., a String)
Step #2: Write a class with public static type conversion methods, annotated with #TypeConverter, to do the conversion (e.g., HashSet<String> to String, String to HashSet<String>), in some safe fashion (e.g., use Gson, formatting your String as JSON)
Step #3: Add a #TypeConverters annotation to your RoomDatabase or other scope, to teach Room about your #TypeConverter methods
For example, here are a pair of type converter methods for converting a Set<String> to/from a regular String, using JSON as the format of the String.
#TypeConverter
public static String fromStringSet(Set<String> strings) {
if (strings==null) {
return(null);
}
StringWriter result=new StringWriter();
JsonWriter json=new JsonWriter(result);
try {
json.beginArray();
for (String s : strings) {
json.value(s);
}
json.endArray();
json.close();
}
catch (IOException e) {
Log.e(TAG, "Exception creating JSON", e);
}
return(result.toString());
}
#TypeConverter
public static Set<String> toStringSet(String strings) {
if (strings==null) {
return(null);
}
StringReader reader=new StringReader(strings);
JsonReader json=new JsonReader(reader);
HashSet<String> result=new HashSet<>();
try {
json.beginArray();
while (json.hasNext()) {
result.add(json.nextString());
}
json.endArray();
}
catch (IOException e) {
Log.e(TAG, "Exception parsing JSON", e);
}
return(result);
}
I created the following class and now it works. Thank you, CommonsWare!
public class Converters {
private static final String SEPARATOR = ",";
#TypeConverter
public static HashSet<String> fromString(String valueAsString) {
HashSet<String> hashSet = new HashSet<>();
if (valueAsString != null && !valueAsString.isEmpty()) {
String[] values = valueAsString.split(SEPARATOR);
hashSet.addAll(Arrays.asList(values));
}
return hashSet;
}
#TypeConverter
public static String hashSetToString(HashSet<String> hashSet) {
StringBuilder stringBuilder = new StringBuilder();
for (String currentElement : hashSet) {
stringBuilder.append(currentElement);
stringBuilder.append(SEPARATOR);
}
return stringBuilder.toString();
}
}

Mockito Android Unit testing

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

Unit testing: NoSuchMethodError at mock retrofit get request

When I run only method testInsert() test completes with no problem, but when I run a whole class I've got an error:
java.lang.NoSuchMethodError: com.dataart.kvarivoda.myapplication.api.PlacesApi.getNearbyPlaces(Ljava/lang/String;Ljava/lang/String;I)Lretrofit2/Call;
at GetPlacesTest.testLogin(GetPlacesTest.java:61)
....
Retrofit #GET interface is:
public interface PlacesApi {
#GET("/maps/api/place/nearbysearch/json")
PlacesResults getNearbyPlaces(#Query("key") String key, #Query("location") String location, #Query("radius") int radius);
}
My test class is:
public class GetPlacesTest extends ProviderTestCase2 {
PlacesApi mockApi;
EventBus mockEventBus;
PlacesApi api;
Cursor cursor;
String webApiKey;
public GetPlacesTest() {
super(PlacesContentProvider.class, "com.example.myName.myapplication.database.PROVE");
}
public void setUp() throws Exception {
super.setUp();
setContext(InstrumentationRegistry.getTargetContext());
//MockitoAnnotations.initMocks(this);
mockApi = Mockito.mock(PlacesApi.class);
mockEventBus = Mockito.mock(EventBus.class);
webApiKey = getContext().getResources().getString(R.string.webApiKey);
api = ((PlacesApp) getApplication()).getApi();
}
#Override
protected void tearDown() throws Exception {
super.tearDown();
if (cursor != null) {
cursor.close();
}
}
#Test
public void testInsert() throws Exception {
PlacesResults results1 = loadResults("getplaces.json");
Mockito.when(mockApi.getNearbyPlaces(eq("testkey"), Matchers.anyString(), Matchers.anyInt())).thenReturn(results1);
GetPlacesAction action = new GetPlacesAction(getContext().getContentResolver(), mockEventBus, mockApi, "testkey");
action.downloadPlaces();
//check
cursor = getContext().getContentResolver().query(PlacesContentProvider.getUri(DB.PlaceTable.DB_TABLE), null, null, null, null);
assertEquals(2, cursor.getCount());
cursor.moveToPosition(0);
assertEquals("461d123aeb1c1648abdd5e535989d2bc518cf28e", getColumn(DB.PlaceTable.COLUMN_ID));
assertEquals("Astral Tower & Residences", getColumn(DB.PlaceTable.COLUMN_TITLE));
assertEquals(getImageUrl("CoQBcwAAAMUTbLLt7doNLiVSnpGryeIJLVdrDnMPcqs3uV84zfvDklrBr1uYxitVMEZWzTD40xkM923ak8HfRtoGiNdi32mqzP6sKB3lOYNbbOQeaHZ3bStClwhWO3507ryh4bODvEfXc-l42r7rFXFAg9GLSd7N2tqoOgLwzTLray0d1sixEhAZaZ2_ajvBieZvUuPA72d7GhQTtFtpqT8j7UBYSHvq9AuRsoRSig"), getColumn(DB.PlaceTable.COLUMN_IMAGE));
assertEquals(locationToString(-33.868111), getColumn(DB.PlaceTable.COLUMN_LOCATION_LAT));
assertEquals(locationToString(151.195219), getColumn(DB.PlaceTable.COLUMN_LOCATION_LNG));
}
private PlacesResults loadResults(String file) throws IOException {
InputStream is = InstrumentationRegistry.getContext().getAssets().open(file);
return new GsonBuilder().create().fromJson(new InputStreamReader(is), PlacesResults.class);
}
private String getImageUrl(String photoReference) {
return new GoogleImagesUtil("testkey").getImageUrl(photoReference);
}
private String locationToString(double location) {
return String.valueOf(LocationsUtil.locationToInteger(location));
}
private String getColumn(String column) {
return cursor.getString(cursor.getColumnIndex(column));
}
private int getColumnInt(String column) {
return cursor.getInt(cursor.getColumnIndex(column));
}
}
After some trying I couldn't even compile and started to get “No tests found” error. Later I've found that there was some run configuration on the class, although I didn't add anything. I deleted run configuration following this answer:
https://stackoverflow.com/a/38190125/2574228
All the errors gone after that.

Categories

Resources