Showing posts with label mockito. Show all posts
Showing posts with label mockito. Show all posts

Wednesday, July 25, 2018

Verify a method that was called from another method

Leave a Comment

I must say, I'm relatively new with android tests and I have a problem which I don't know how to solve.

My idea is to verify that a method is called whenever I use another method which I keep failing with mockito (normally it shouldn't fail).

Keep in mind that I'm using Robolectric framework and just wanted to test with Mockito for verifying the number of invocations.

Imagine that I have an Object instanced :

        FirebaseManager firebaseManager = FirebaseManager_WithCacheSetTokenToNullAndSendingStatusSetToFalsePlusFirebaseCloudMessaging_ImplementedInApplicationButTokenNotRetrievedYet(); 

I know that mockito has a feature where you can : Verifying exact number of invocations / at least x / never

Now, my purpose is to verify that whenever i call the method setToken(token_from_google) where token_from_google is different of null, It will execute, inside, the other method saveState. However if the token is null, then the method saveState shouldn't be executed. I tried the following example by checking that post

//        FirebaseManager spy = spy(firebaseManager); //        spy.setToken(token_from_google); //        verify(spy, times(1)).saveState(); 

But it always keep failing and saying the following :

Wanted but not invoked: firebaseManager.saveState(); -> at  com.android.FirebaseManager.saveState(FirebaseManager.java:141)  However, there was exactly 1 interaction with this mock: firebaseManager.setToken("Random_token"); -> at com.android.FirebaseManagerTest.testSetToken(FirebaseManagerTest.java:599)   Wanted but not invoked: firebaseManager.saveState(); -> at com.android.FirebaseManager.saveState(FirebaseManager.java:141)  However, there was exactly 1 interaction with this mock: firebaseManager.setToken("Random_token"); -> at com.android.FirebaseManagerTest.testSetToken(FirebaseManagerTest.java:599) 

Does anyone know which direction I should take in order to solve my situation ? Thank you

P.S.: As requested I'm showing the class implementation that I did :

package com.followapps.android;  import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable;  import com.followapps.android.internal.Configuration; import com.followapps.android.internal.service.RequestService; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import com.google.firebase.FirebaseApp; import com.google.firebase.iid.FirebaseInstanceId;  /** * Class which manages the Firebase methods. */ public class FirebaseManager implements IFirebaseManager { private static final String TAG = FirebaseManager.class.getSimpleName();   private FirebaseManager(){}  /**  * Check if no new token was generated and if the token was sent and received with success to FA server.  */ @Override public boolean isTokenValidAndSent(FirebaseInstanceId firebaseInstanceId) {     final String tokenFromFirebase = firebaseInstanceId.getToken();     boolean isTokenSent = getSendingStatusTokenPreferences();     boolean isANewTokenAvailable = isTokenNew(tokenFromFirebase);      return isANewTokenAvailable || !isTokenSent; }  /**  * Fetch the token of Firebase.  * This method will compare the local token and the token retrieved from Firebase Instance.  */ @Nullable @Override public String getToken(FirebaseInstanceId firebaseInstanceId) {     String res = null;      final String tokenFromFirebase = firebaseInstanceId.getToken();     boolean isTokenSent = getSendingStatusTokenPreferences();     boolean isANewTokenAvailable = isTokenNew(tokenFromFirebase);      if (isANewTokenAvailable) {         saveTokenPreferences(tokenFromFirebase);         res = tokenFromFirebase;     } else if (!isTokenSent) {         res = getTokenPreferences();     }     return res; }  /**  * This method sends the token to FA servers.  * First, it will save to our local cache memory, then he will send the token to our {@link RequestService} class.  */ @Override public void sendToken(@Nullable String token) {     if(token != null) {         saveTokenPreferences(token);         saveSendingStatusTokenPreferences(false);         RequestService.requestPushNotificationToken(token);     } }  /**  * This method compare the token from our local cache memory and the token retrieved from Firebase instance.  *  * @return true if token is different or tokenFromCache equals to null but fetchedTokenFromFirebase has a value. false if tokens are equals or both tokens are nullable  */ private boolean isTokenNew(@Nullable final String fetchedTokenFromFirebase) {     String tokenFromCache = getTokenPreferences();     boolean res = false;      if (fetchedTokenFromFirebase != null) {         if (tokenFromCache != null) {             res = !tokenFromCache.equals(fetchedTokenFromFirebase);         } else {             res = true; //the case that we have a new token from firebase and nothing in cache => this is a new token         }     }     return res; }  /**  * This method saves the token to our local cache memory  */ private void saveTokenPreferences(@NonNull final String token) {     //Configuration     Configuration.savePushTokenNotification(token); }  /**  * This method retrieves the token from our local cache memory.  */ @Nullable public String getTokenPreferences() {     return Configuration.getPushTokenNotification(); }  /**  * This method saves the sending status of our token sent to FA servers.  */ @Override public void saveSendingStatusTokenPreferences(boolean tokenSent) {     Configuration.saveSendingStatusPushTokenNotification(tokenSent); }  /**  * This method gets the sending status of a token.  */ public boolean getSendingStatusTokenPreferences() {     return Configuration.getSendingStatusPushTokenNotification(); }  /**  * Class for building the FirebaseManager, it will check if the google play services is available and Firebase implemented.  */ public final static class Builder {      private int googlePlayServicesAvailable;     private boolean firebaseImplemented;      private Builder(){}      // for production     public Builder(Context mContext) {         this(GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(mContext), FirebaseApp.initializeApp(mContext)!=null);     }      // for test     public Builder(int isGooglePlayServicesAvailable, boolean firebaseImplemented) {         this.googlePlayServicesAvailable = isGooglePlayServicesAvailable;         this.firebaseImplemented = firebaseImplemented;     }      /**      * Check the status of Google Play Services in order to run Firebase      */     private boolean isGooglePlayServicesAvailable(){         boolean res = true;         if (this.googlePlayServicesAvailable != ConnectionResult.SUCCESS) {             res = false;         }         return res;     }      public boolean isFirebaseAndGooglePlayServicesWorking(){return this.isGooglePlayServicesAvailable() && this.firebaseImplemented; }       /**      * Create the FirebaseManager object      *      * @return Interface, but the object might be a null object in cases that firebase is not implemented and google play services not available or an object with full interaction with Firebase API.      * */     public IFirebaseManager build() {         return isFirebaseAndGooglePlayServicesWorking() ? new FirebaseManager() : new FirebaseManagerDeactivated();     }  } } 

0 Answers

Read More

Sunday, April 16, 2017

SecurityProvider in Mockito Tests when run alongside Robolectric

Leave a Comment

We haven an Android project where we use MockitoTestRunner and RobolectricTestRunner for different kinds of tests.

I have written a set of unit test that are concerned with SSL, thus loading certs/keystores/truststores, etc. For this I used the MockitoJUnitRunner and added the Bouncycastle provider programmatically as such:

Security.insertProviderAt(new BouncyCastleProvider(), 1); 

Now, these tests run perfectly fine when run on their own - e.g. when I directly run single methods from the test classes, or run these classes from the Project tree menu, they work just fine.

But when I run said tests along side ANY test which uses the RobolectricTestRunner (such as if I just run all the tests in my project together before committing), I get the following exception:

java.io.IOException: error constructing MAC: java.lang.SecurityException: JCE cannot authenticate the provider BC 

I'm baffled. How will the testrunner used in one test class affect the run of other classes, especially if we use a different test runner?


Additional info:

  • The exception only occurs once I actually try do do something with the BC provider (e.g. the first time the test tries to load a PKCS12 certificate) - the insertProviderAt(...) call itself seems to pass fine...
  • Also when I print out the list of providers for each test run, I see that Robolectric already has a BC provider in there, but is still failing when I try to use it.
  • Also if I don't add the BC provider, the tests still fail with the same error when run in a test suite alongside Robolectric tests. When run alone they fail with java.security.NoSuchProviderException: no such provider: BC, as we're specifying the provider explicitly.

0 Answers

Read More

Tuesday, September 27, 2016

Mocking method calls using power mockito - org.powermock.api.mockito.ClassNotPreparedException

Leave a Comment

I have an image loader class and i need to test some static methods in it. Since Mockito does not support static methods i switched to Power Mockito. But the static method i am testing has a method call

 Base64.encodeToString(byteArray, Base64.DEFAULT); 

To mock this i am using mockStatic method as below with @PrepareForTest annotation.

 PowerMockito.mockStatic(Base64.class); 

But Android studio is returning me still returning me an error as below.

org.powermock.api.mockito.ClassNotPreparedException: The class android.util.Base64 not prepared for test. To prepare this class, add class to the '@PrepareForTest' annotation.

Below is my complete code.

Code to be tested:

import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Base64; import android.widget.ImageView;    public static String convertBitmapToBase64(Bitmap imageBitmap, boolean withCompression) {     ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();     imageBitmap.compress(Bitmap.CompressFormat.PNG, 120, byteArrayOutputStream);     byte[] byteArray = byteArrayOutputStream.toByteArray();     return Base64.encodeToString(byteArray, Base64.DEFAULT); } 

Test class code

import android.graphics.Bitmap; import android.util.Base64; import org.junit.Before; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.testng.annotations.Test;  @RunWith(PowerMockRunner.class) @PrepareForTest({Base64.class}) public class ImageLoaderTest  { @Test    public void testConvertBitmap(){     byte[] array = new byte[20];     PowerMockito.mockStatic(Base64.class);     PowerMockito.when(Base64.encodeToString(array, Base64.DEFAULT)).thenReturn("asdfghjkl");     Bitmap mockedBitmap= PowerMockito.mock(Bitmap.class);     String output = ImageLoaderUtils.convertBitmapToBase64(mockedBitmap);     assert (!output.isEmpty()); } 

}

Gradle dependencies

testCompile 'junit:junit:4.12' testCompile 'org.powermock:powermock:1.6.5' testCompile 'org.powermock:powermock-module-junit4:1.6.5' testCompile 'org.powermock:powermock-api-mockito:1.6.5' 

2 Answers

Answers 1

Short answer you can't. Here from the FAQ:

What are the limitations of Mockito

  • Cannot mock final classes
  • Cannot mock static methods
  • Cannot mock final methods - their real behavior is executed without any exception. Mockito cannot warn you about mocking final methods so be vigilant.

Further information about this limitation:

Can I mock static methods?

No. Mockito prefers object orientation and dependency injection over static, procedural code that is hard to understand & change. If you deal with scary legacy code you can use JMockit or Powermock to mock static methods.

If you want to use PowerMock try like this:

@RunWith(PowerMockRunner.class) @PrepareForTest( { Base64.class }) public class YourTestCase {     @Test     public void testStatic() {         mockStatic(Base64.class);         when(Base64.encodeToString(argument)).thenReturn("expected result");     } } 

More on powermock test sample

Answers 2

Really trivially, I think you're calling prepareForTest incorrectly. It should be prepareForTest(Base64.class) not prepareForTest({Base64.class}) (note the squiggly braces in your code). Given the error you're getting, I think that's relevant.

Read More

Thursday, August 11, 2016

Using mockito to test function that uses a context

Leave a Comment
Android Studio 2.1.2 

I am trying to test getJsonFromResource which calls loadNewsFeed. I want to be able to test 2 cases 1 where loadNewsFeed will return an empty string and the other where it will return some json string.

So I am trying to mock the loadNewsFeed function to return an empty string. However, when the concrete getJsonFromResource is called it will call the real loadNewsFeed and cause a null pointer exception. This is what I have tried in my test comments explaining what I am doing:

@Test public void shouldFailIfJSONStringIsEmpty() throws Exception {     /* Mock Context class */     Context context = mock(Context.class);     /* initialize the concrete parseNewsFeed passing in the fake context */     ParseNewsFeed parseNewsFeed = new ParseNewsFeed(context);     /* Create a mock of the parseNewsFeed so a fake call to loadNewsFeed will return an empty string */     ParseNewsFeed mockParseNewsFeed = mock(ParseNewsFeed.class);     /* Mock the events that will be verified */     ParseNewsFeedContract.Events<Status> mockEvents = mock(ParseNewsFeedContract.Events.class);      /* Return an empty string when loadNewsFeed is called */     when(mockParseNewsFeed.loadNewsFeed()).thenReturn("");      /* Called the concrete getJsonFromResource */     parseNewsFeed.getJsonFromResource(mockEvents);      /* verify that onNewsFailure was called once and onNewsSuccess was never called */     verify(mockEvents, times(1)).onNewsFailure(anyString());     verify(mockEvents, never()).onNewsSuccess(any(Status.class)); } 

This is the class I am trying to test.

public class ParseNewsFeed implements ParseNewsFeedContract {     private Context mContext;      public ParseNewsFeed(Context context) {         if(context != null) {             Timber.d("mContext != null");             mContext = context;         }     }      /**      * Get the json from the local resource file and add to the cache to save loading each time      * @return the json in string representation      */     @Override     public void getJsonFromResource(Events<Status> events) {         /* Get the json in string format */         final String jsonString = loadNewsFeed();          /* Check that is contains something */         if(!jsonString.isEmpty()) {             try {                 final Status status = new Gson().fromJson(jsonString, Status.class);                 if(status != null) {                     Timber.d("url: %s", status.getResults().get(0).getMultimedia().get(0).getUrl());                     events.onNewsSuccess(status);                 }                 else {                     Timber.e("status == null");                     events.onNewsFailure("Failed to get results from json");                 }             }             catch (JsonSyntaxException e) {                 Timber.e("Invalid JSON: %s", e.getMessage());                 events.onNewsFailure(e.getMessage());             }         }     }      /**      * Opens and reads from the news_list and writes to a buffer      * @return return the json representation as a string or a empty string for failure      */     public String loadNewsFeed() {         InputStream inputStream = mContext.getResources().openRawResource(R.raw.news_list);         Writer writer = new StringWriter();         char[] buffer = new char[1024];          try {             InputStreamReader inputReader = new InputStreamReader(inputStream, "UTF-8");             BufferedReader bufferReader = new BufferedReader(inputReader);             int n;             while ((n = bufferReader.read(buffer)) != -1) {                 writer.write(buffer, 0, n);             }              inputStream.close();         }         catch (IOException ioException) {             return "";         }          return writer.toString();     } } 

3 Answers

Answers 1

First of all, the reason why your original code doesn't work is because there's no relationship between your two objects parseNewsFeed and mockParseNewsFeed, hence the stubbing that you do for the mockParseNewsFeed doesn't have any effect when you invoke parseNewsFeed.getJsonFromResource(mockEvents). Using spy as David Wallace suggested would work, but if I were you, I would rewrite the code a bit differently to make it even easier to test.

One observation is that the code in loadNewsFeed() method doesn't seem to have a strong relationship with the ParseNewsFeed class, so I'd extract this code into an object (e.g. NewsFeedLoader), and then have this object as a dependency of ParseNewsFeed class. Then you can mock this Loader easily (return "" or any string that you want when passing a Context and possibly the R.raw.news_list id as well). With this Loader class, you can even unit test it separately from the ParseNewsFeed, and being able to improve the Loader however you want to (e.g. a better way to read a raw resource) without affecting the ParseNewsFeed class.

Answers 2

It looks like you want to have a ParseNewsFeed object where the loadNewsFeed method has been stubbed, but other methods work correctly. The simplest way to get that would probably be to create a spy, something like

ParseNewsFeed spyParseNewsFeed = Mockito.spy(new ParseNewsFeed(context)); Mockito.doReturn("").when(spyParseNewsFeed).loadNewsFeed(); 

Answers 3

Use when() and then() methods of your mocked context. It is actually described in example of official tutorial here.

@Mock Context mMockContext;  @Test public void readStringFromContext_LocalizedString() {     // Given a mocked Context injected into the object under test...     when(mMockContext.getString(R.string.hello_word))             .thenReturn(FAKE_STRING);     ClassUnderTest myObjectUnderTest = new ClassUnderTest(mMockContext);      // ...when the string is returned from the object under test...     String result = myObjectUnderTest.getHelloWorldString();      // ...then the result should be the expected one.     assertThat(result, is(FAKE_STRING)); 
Read More

Friday, March 11, 2016

Android Unit Test with Retrofit2 and Mockito or Robolectric

Leave a Comment

Can I test real response from retrofit2beta4? Do i need Mockito or Robolectic?

I don't have activities in my project, it will be a library and I need to test is server responding correctly. Now I have such code and stuck...

@Mock ApiManager apiManager;  @Captor private ArgumentCaptor<ApiCallback<Void>> cb;  @Before public void setUp() throws Exception {     apiManager = ApiManager.getInstance();     MockitoAnnotations.initMocks(this); }  @Test public void test_login() {     Mockito.verify(apiManager)            .loginUser(Mockito.eq(login), Mockito.eq(pass), cb.capture());     // cb.getValue();     // assertEquals(cb.getValue().isError(), false); } 

I can make fake response, but I need to test real. Is it success? Is it's body correct? Can you help me with code?

2 Answers

Answers 1

It is generally not a good idea to test real server requests. See this blog post for an interesting discussion on the topic. According to the author, using your real server is a problem because:

  • Another moving piece that can intermittently fail
  • Requires some expertise outside of the Android domain to deploy the server and keep it updated
  • Difficult to trigger error/edge cases
  • Slow test execution (still making HTTP calls)

You can avoid all the issues above by using a mock server such as OkHttp's MockWebServer to simulate real response results. For example:

@Test public void test() throws IOException {     MockWebServer mockWebServer = new MockWebServer();      Retrofit retrofit = new Retrofit.Builder()             .baseUrl(mockWebServer.url("").toString())             //TODO Add your Retrofit parameters here             .build();      //Set a response for retrofit to handle. You can copy a sample     //response from your server to simulate a correct result or an error.     //MockResponse can also be customized with different parameters     //to match your test needs     mockWebServer.enqueue(new MockResponse().setBody("your json body"));      YourRetrofitService service = retrofit.create(YourRetrofitService.class);      //With your service created you can now call its method that should      //consume the MockResponse above. You can then use the desired     //assertion to check if the result is as expected. For example:     Call<YourObject> call = service.getYourObject();     assertTrue(call.execute() != null);      //Finish web server     mockWebServer.shutdown(); } 

If you need to simulate network delays, you can customize your response as follows:

MockResponse response = new MockResponse()     .addHeader("Content-Type", "application/json; charset=utf-8")     .addHeader("Cache-Control", "no-cache")     .setBody("{}"); response.throttleBody(1024, 1, TimeUnit.SECONDS); 

Alternatively, you can use MockRetrofit and NetworkBehavior to simulate API responses. See here an example of how to use it.

Finally, if you just want to test your Retrofit Service, the easiest would be to create a mock version of it that emits mock results for your tests. For example, if you have the following GitHub service interface:

public interface GitHub {     @GET("/repos/{owner}/{repo}/contributors")     Call<List<Contributor>> contributors(         @Path("owner") String owner,         @Path("repo") String repo); } 

You can then create the following MockGitHub for your tests:

public class MockGitHub implements GitHub {     private final BehaviorDelegate<GitHub> delegate;     private final Map<String, Map<String, List<Contributor>>> ownerRepoContributors;      public MockGitHub(BehaviorDelegate<GitHub> delegate) {         this.delegate = delegate;         ownerRepoContributors = new LinkedHashMap<>();          // Seed some mock data.         addContributor("square", "retrofit", "John Doe", 12);         addContributor("square", "retrofit", "Bob Smith", 2);         addContributor("square", "retrofit", "Big Bird", 40);         addContributor("square", "picasso", "Proposition Joe", 39);         addContributor("square", "picasso", "Keiser Soze", 152);     }      @Override public Call<List<Contributor>> contributors(String owner, String repo) {         List<Contributor> response = Collections.emptyList();         Map<String, List<Contributor>> repoContributors = ownerRepoContributors.get(owner);         if (repoContributors != null) {             List<Contributor> contributors = repoContributors.get(repo);             if (contributors != null) {                 response = contributors;             }         }         return delegate.returningResponse(response).contributors(owner, repo);     } } 

You can then use the MockGitHub on your tests to simulate the kinds of responses you are looking for. For the full example, see the implementations of the SimpleService and SimpleMockService for this Retrofit example.

EDIT: If you absolutely must connect to the actual server, you can set Retrofit to work synchronously with a custom ImmediateExecutor:

public class ImmediateExecutor implements Executor {     @Override public void execute(Runnable command) {         command.run();     } } 

Then apply it to the OkHttpClient you use when building the Retrofit:

OkHttpClient client = OkHttpClient.Builder()         .dispatcher(new Dispatcher(new ImmediateExecutor()))         .build();  Retrofit retrofit = new Retrofit.Builder()         .client(client)         //Your params         .build(); 

Answers 2

The answer is too easy than i expected:

Using CountDownLatch makes your test wait until you call countDown()

public class SimpleRetrofitTest {  private static final String login = "your@login; private static final String pass = "pass"; private final CountDownLatch latch = new CountDownLatch(1); private ApiManager apiManager; private OAuthToken oAuthToken;  @Before public void beforeTest() {     apiManager = ApiManager.getInstance(); }  @Test public void test_login() throws InterruptedException {     Assert.assertNotNull(apiManager);     apiManager.loginUser(login, pass, new ApiCallback<OAuthToken>() {         @Override         public void onSuccess(OAuthToken token) {             oAuthToken = token;             latch.countDown();         }          @Override         public void onFailure(@ResultCode.Code int errorCode, String errorMessage) {             latch.countDown();         }     });     latch.await();     Assert.assertNotNull(oAuthToken); }  @After public void afterTest() {     oAuthToken = null; }} 
Read More