Mocking Static Functions in Android - android

Is there any way I can Mock Static Function in Android using any Mocking Framework.
Mockito can mock classes but is insuffiecient to mock Static functions.
Any help will be highly appreciated.
Thanks in Advance

Mocking works by using the concepts of Object Orientation, Inheritance etc....
Basically by overriding certain methods & behaviour in objects / instances that look like real objects, because they are subclasses of these real objects.
In other words, the mocking part comes in overriding methods on instances.
It is not possible to override a static method (afaik).
Therefore mocking of static calls is not easy (if even possible).
EDIT - I was wrong...
As it turns out, I was wrong in my above statement that it is not possible.
I should have searched this site for duplicate questions. See below for some links to frameworks that claim to do this for you in some cases. Since they work with bytecode, I'm not sure they will work properly on Android (ymmv).
Mocking Static Methods
How can I easily mock out a static method in Java (jUnit4)
(thanks to Rohit for forcing me to reassess my beliefs)

Please try this instead: https://bintray.com/linkedin/maven/dexmaker-mockito-inline-extended
It helps me successfully mock the static method in the Android Instrumented Tests, but note that this feature requires running on a device with at least Android P.
Here is what I did:
Replace the androidTestImplementation 'org.mockito:mockito-android:2.28.0' with androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline-extended:2.28.0'
Then mock the static method like this:
static class StaticTrojan {
static String staticOpen() { return "horse"; }
}
#Test
public void testStubbingStaticMethod() {
MockitoSession session = mockitoSession().spyStatic(StaticTrojan.class).startMocking();
try {
when(StaticTrojan.staticOpen()).thenReturn("soldiers");
assertEquals("soldiers", StaticTrojan.staticOpen());
} finally {
session.finishMocking();
}
// Once the session is finished, all stubbings are reset
assertEquals("horse", StaticTrojan.staticOpen());
}

If you use Kotlin, then to mocking static functions you can connect the mockk library project:
androidTestImplementation "io.mockk:mockk-android:1.12.0"
Then you need to add a AndroidManifest.xml to the androidTest directory if your tests are located in the application module.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="you.application.package">
<application
android:debuggable="true"
android:extractNativeLibs="true" />
</manifest>
Then you can mocking static functions using the following code:
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import org.junit.Assert.assertEquals
import org.junit.Test
class TestMockingStaticFunction {
object StaticTrojan {
#JvmStatic
fun staticOpen(): String {
return "horse"
}
}
#Test
fun testMockingStaticFunction() {
assertEquals("horse", StaticTrojan.staticOpen())
mockkStatic(StaticTrojan::staticOpen)
val mockScope = every { StaticTrojan.staticOpen() } returns "solders"
assertEquals("solders", StaticTrojan.staticOpen())
unmockkStatic(StaticTrojan::staticOpen)
assertEquals("horse", StaticTrojan.staticOpen())
}
}
Library API allows you to comfortably mock the Kotlin objects, in the example above the object is used only to create a static function using #JvmStatic annotation.
Attention! This approach uses JVMTI available in Android P starting from the API level 28. Your application can be written using a smaller API, but the tests you must run only on Android P devices or newer.

Related

How can I mock and test this class?

The follows was the code which I want to test.
public class Demo {
private static final List<Pair<String, String>> mList;
static {
mList = new ArrayList<>();
mList.add(new Pair<>("F0", "T1"));
mList.add(new Pair<>("F1", "T2"));
mList.add(new Pair<>("F2", "T3"));
}
public String getStr(int pos) {
return mList.get(pos).first;
}
}
I was an android developer. I have get some trouble in test and mock the code.I have use mockito.
I have try some code to test it,but the result was not my expect.
1.First try
#Test
public void test(){
Demo demo=new Demo();
assertEquals(demo.getStr(0),"F0");
/**
* java.lang.AssertionError:
* Expected :null
* Actual :F0
*/
}
2.Second try
#Test
public void test() {
Demo demo = mock(Demo.class);
doCallRealMethod().when(demo).getStr(0);
assertEquals(demo.getStr(0), "F0");
/**
* java.lang.AssertionError:
* Expected :null
* Actual :F0
*/
}
Anyone tell me how can I resolve this problem to make demo.getStr(0) == "F0" by call the real method? Thanks!
===========================
Another question relate to it
I have try an another test to test android.util.Pair class, and the result is that "pair.first" was null,.(There are androidTest and test directory,I put it into test package.Did it impact the result?)
import android.util.Pair;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
public class DemoTest {
#Test
public void test1(){
Pair<String,String> pair=new Pair("First","Second");
assertEquals("First",pair.first);
//pair.first was null,why?
}
#Test
public void test2(){
Pair<String,String> pair= Mockito.spy(Pair.class);
assertEquals("First",pair.first);
//pair.first was null also,why?
}
}
Why the simple code is correct in real android environment,but failure in test?
I had the same problem too. month ago I have problem with TextUtils class too.
I report this to jUnit but they told me the problem is with android package because in unit test environment you don't have access to platform specific classes
for that pair case you can use this package. this works for me
import android.support.v4.util.Pair;
The problem in your first try is, that the public field "first" is actually null.
Is the Pair class the one from the "javafx.util" package or a custom implementation?
Did you forget "this.first = first" or something similar in the constructor of the "Pair" class?
I would also recommend to change the following line:
assertEquals(demo.getStr(0),"F0");
to
assertEquals("F0", demo.getStr(0));
so that the error is printed correctly.
Your second try does not make any sense. What is the point in mocking the class you want to test?
I think the second example has the same problem as the first one. Pair.first is never set. If you fix that, it should also work (untested).
From Google's Android tools website:
"Method ... not mocked."
The android.jar file that is used to run unit tests does not contain any actual code - that is provided by the Android system image on real devices. Instead, all methods throw exceptions (by default). This is to make sure your unit tests only test your code and do not depend on any particular behaviour of the Android platform (that you have not explicitly mocked e.g. using Mockito).
So how can we solve this?
In other words. If you need a default android class to work properly you either have to include it from a separate repository, or implement it yourself.
In the case of Android's Pair class. You can use android.support.v4.util.Pair instead.
To get access to this class, you can include com.android.support:support-compat:27.0.0 in your build.gradle or dependencies file.
If you are not using Gradle, you can copy the implementation of this file and use it in place of the official one. Or you can try and download the .jar file from this older version https://mvnrepository.com/artifact/com.google.android/support-v4/r7 (I have not tested whether it works)
Another approach (based on this) is to create the class in app/src/test/java/android/util/Pair.java and copy the code from the Android implementation.
This way you don't need extra dependencies. (There may be issues related to the implementation changing after you make the copy, but the dependencies may become stale as well.)

How do you cast RuntimeEnvironment.application?

When running Robolectric tests, RuntimeEnvironment.application's type is determined by your configuration. Say I configured RoboApplication.class as my test application, I can cast RuntimeEnvironment.application to my type without fail.
RoboApplication app = (RoboApplication) RuntimeEnvironment.application;
app.doSomething();
However, once I integrate PowerMock, the cast line fails with
java.lang.ClassCastException: RoboApplication cannot be cast to RoboApplication
How can I workaround this issue?
This is a problem because PowerMock and Robolectric are mutually incompatible due to the use of their own classloaders.
Even though the names are the same, the Class objects aren't actually the same: Robolectric and PowerMock both work by loading the test through custom classloaders. These classloaders change the classes in question, allowing you to replace static/final Android system classes and methods [Robolectric] or all static/final classes [PowerMock]. This is part of the reason that PowerMock and Robolectric both rely on having their own JUnit4 Runner: That way they can load the appropriate classes from their own modifying classloaders.
Because of this, the instances can't be cast to one anothers' classes, even though they have the same name and originate from the same source file: Each framework can change the class implementation, so they aren't necessarily compatible with one another.
You'll need to choose one framework or the other: Use Robolectric shadows, possibly with EasyMock or Mockito directly, or use PowerMock to stub the Android infrastructure calls yourself manually.
See also:
ClassCastException when casting to the same class
cast across classloader?
I needed also an app reference in order to start a Dagger2 module. After several attempts and getting the same cast exception error you are getting I made my app as follows
public class App extends Application {
private static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
if( appComponent==null ){
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
}
public static AppComponent getAppComponent() {
return appComponent;
}
public static void setAppComponent(AppComponent component){
appComponent = component;
}
}
And within my Robolectric/PowerMock tester:
#Before
public void before() throws Exception {
App appMocked = PowerMockito.mock(App.class);
App.setAppComponent(DaggerAppComponent.builder().appModule(new AppModule(appMocked)).build());
....
}
Then my activity simply called up for App.getAppComponent().inject(this);
FYI, I tried not to mocked the app class and used ((App)RuntimeEnvironment.application), but that didn't work. I also tried to subclass it, and use it in Robolectric's application configuration, but ended up with the casting issue. So I hope this can be of any help.
Of course, that setter shouldn't go in production. But this is the only way I could figure this to work.

Android annotations not injecting RestService

Hi I can't inject the RestService anywhere in my code.
I'm working test driven so I am using Robolectric to test this code. I hope it isn't a problem with AA+Robolectric, I don't have any experience with this.
The weird thing is that in my tests I can manually insert the generated RestClient_, but it doesn't get inserted automatically.
So I can do this:
RestClient rest = new RestClient_(activity.getApplicationContext());
but the following doesn't work:
#RestService
RestClient restClient;
I get a NullPointerException on restClient.
I also didn't forget the #EBean tag
#EBean
public class Player {
#RestService
RestClient restClient;
private int playerId;
public Player() {
}
public int getPlayerId() {
return playerId;
}
public List<Card> getHand() {
return restClient.getHand(playerId);
}
}
In the log I can see that Android Annotations has processed everything correctly.
This is my first project with Android Annotations and I can't grasp why the dependency injection doesn't work. No dependency injection removes almost all the benefit of using Android Annotations.
Thanks in advance!
Some extra information: I am instantiating my Player object in an Android Annotations-annotated REST Service. A code snippet of the method creating the Player object.
#Get(value = "/players/createAnonymous")
#Accept(MediaType.APPLICATION_JSON)
public Player createAnonymousPlayer();
Ok so here is the answer to my question. I hope it can be of use to other people who ask themselves the same as I did. Thanks to WonderCsabo for pointing me in the right direction.
Android Annotations generates the annotated classes at compile time. So Player becomes Player_. When using #Inject, Android Annotations will inject an instance of Player_ in the annotated field. However in my case the RestClient_ will return Player and not Player_ (So the player object without the processed annotations, so without the injected RestClient_).
It's not possible (not that I know of) to make the RestClient return Player_. This will not compile because the Player_ class doesn't exist yet at compile time.
Now I also understand the drawback that Android Annotations has. You can't easily test the code because you can't use android annotations in your tests. You can't swap the testclass with the generated class anywhere. You can't mock the injected classes either without using a runtime mock generator as PowerMock. So if you need dependency injection and easy testing, you should look elsewhere. (Dagger for example)

Roboguice and mocks: How to have roboguice inject a mock service when testing but use the REAL otherwise?

Just got my feet wet with roboguice, i like it!
I have quite a lot of methods that depend on a DB and LocationManger etc hence when i am testing these it uses the real objects, i would like to mock these objects so that when i am testing i don't have to depend on anything.
I also have been using mockito but i am unsure how i could go about this?
I know the android system comes with various mocks but i think it would be better to roll my own with mockito?
In either case i need to inject them when testing.
Anyone have any ideas on this?
Thanks in advance
Take a look at https://github.com/roboguice/roboguice/blob/master/astroboy/src/test/java/org/roboguice/astroboy/controller/Astroboy2Test.java which uses Modules.override() to override the default module with some test-specific configurations.
#Before
public void setup() {
// Override the default RoboGuice module
RoboGuice.setBaseApplicationInjector(Robolectric.application, RoboGuice.DEFAULT_STAGE, Modules.override(RoboGuice.newDefaultRoboModule(Robolectric.application)).with(new MyTestModule()));
// For roboguice 4.0 and robolectric 3.1.2
RoboGuice.getOrCreateBaseApplicationInjector(RuntimeEnvironment.application, RoboGuice.DEFAULT_STAGE, Modules.override(RoboGuice.newDefaultRoboModule(RuntimeEnvironment.application)).with(new MyTestModule()));
}
Just to expand on this as it was the top hit while I was looking for it...
Once you've set your test class (or test runner) to override the default RoboGuice module then set your overriden RoboGuice Module as (in this instance)
public class TestModule extends AbstractModule {
#Override
protected void configure() {
bind(LocationManager.class).toInstance((LocationManager) Robolectric.application.getSystemService(Context.LOCATION_SERVICE));
}
}
Then RoboGuice will inject the same location manager in your tests as in your application. And you can instantiate a shadow of it and set the expected location, provider state, etc.
#Test
public void mapLoadsCenteredOnPhoneLocationWhenNoTargetIntent() {
Location l = new Location("test");
l.setLatitude(Double.parseDouble("52.222"));
l.setLongitude(Double.parseDouble("-2.222"));
shadowLocationManager.setLastKnownLocation(GPS_PROVIDER, l);
shadowLocationManager.setProviderEnabled(GPS_PROVIDER, true);
shadowLocationManager.setProviderEnabled(NETWORK_PROVIDER, false);
//snip
}
#Martin: As Paul says you can inject your test location manager with Robolectric and Roboguice. However I think it's better if mocking with Mockito, this post is good for starting. You create a Mocked object and bind it to your interface. You can find also example with mocking and injecting.

How do I use jMock's ClassImposteriser for Android unit testing?

In my unit test, I've tried the following:
import org.jmock.Mockery;
import org.jmock.Expectations;
import org.jmock.lib.legacy.ClassImposteriser;
public class MyActivityTest extends ActivityUnitTestCase<MyActivity> {
private Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
...
}
My intended use is to mock my project's Application subclass. However, when I run my tests, I get an java.lang.ExceptionInInitializerError. Can I not use the ClassImposteriser extension for running Android unit tests?
This is something I looked into extensively several months ago. Unfortunately the dalvik VM currently does not support the bytecode manipulations that are required to mock concrete classes.
So you won't be able to use any mocking library to mock a class. You'll have to extract an interface for each class you want to mock in your android tests and mock the interface instead.
Some further reading about the davlik limitations:
http://code.google.com/p/mockito/issues/detail?id=57
https://sites.google.com/site/androiddevtesting/

Categories

Resources