Mockito MissingMethodInvocationException when using mock-maker-inline configuration - android

I am using Mockito to run tests in an android module.
My tests works fine before adding the configuration for mock-maker-inline in src/test/resources/mockito-extensions file. But after adding this configuration some of my tests are not working any more.
Here is the code I'm using to mock my class.
private static Fragment createMock() {
final Fragment stubFragment =
mock(Fragment.class, withSettings().extraInterfaces(MyInterface.class));
when(((MyInterface) stubFragment).getFragment()).thenReturn(stubFragment);
return stubFragment;
}
And this is the exception
org.mockito.exceptions.misusing.MissingMethodInvocationException
I don't need Mockito inline for this class as it works without it but I need it for some other classes in the same module. Any help or suggestions will be appreciated

Related

Android plain Junit with Dagger 2

I used to work in MVP and I usually test my presenters using a plain Junit (Not the Instrumentation !) , since Presenters only have the business logic and no references to Android internals whatsoever.
Now by switching to Dagger 2 , I understood that I have a problem setting up a "TestModule" for my app component.
Creating a component will not work from within a test class (probably because "apt" is not running there)
Didn't find any examples for using Dagger with a standard Junit testing. Every example I have found only relies on Instrumentation testing or Roboelectric (which basically mocks Activities and other Android related stuff) , but this is just a UI testing for me , and I don't need that.
Just to make things clear , I am talking about the tests that are located at app->src->test folder not the app->src->androidTest !
So do I do something wrong ? Or missing something ? Can anyone explain or give examples on how to use Dagger 2 in normal unit tests ?
I'm not sure if my solution will work for you but I see no reason it shouldn't.
First I created testInjectionComponent
#Singleton
#Component(modules = {MockNetworkModule.class})
public interface MockInjectionComponent extends InjectionComponent {
void inject(DaggerUnitTest daggerUnitTest);
}
Then my Unit Tests I add injection in the before method. like so:
#Before
public void setUp() throws Exception {
MockInjectionComponent mockInjectionComponent = DaggerMockInjectionComponent
.builder()
.mockNetworkModule(new MockNetworkModule())
.build();
mockInjectionComponent.inject(this);
}
Then I just Annotate my Injected Object.
EDIT :
Do not forget to add testApt "com.google.dagger:dagger-compiler:$daggerVersion" at your app.gradle file .
As mentioned by the accepted answer. Do not forget to add :
For Java
Android Test
androidTestAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'
JUnit test
testAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'
For Kotlin
Android Test
kaptAndroidTest 'com.google.dagger:dagger-compiler:$dagger_version'
JUnit test
kaptTest 'com.google.dagger:dagger-compiler:$dagger_version'
You don't need any dagger to test your presenter. Dagger's job is it to fullfill the dependencies of your classes (dependency injection).
For example you have this Presenter:
public class MyPresenter {
Database database;
ApiService apiService;
#Inject
public MyPresenter(final Database database, final ApiService apiService) {
this.database = database;
this.apiService = apiService;
}
}
Dagger will provide your Presenter with the database and apiService objects for your presenter to use them. When running the actual app (not a test) these will be real objects with real functionality.
When testing the presenter, you want to test only the presenter, everything else should be mocked.
So when you create the presenter in your PresenterTest, you create it with mocked versions of database and apiService.
You can then test how your presenter interacts with these object by
a. mocking the objects behaviour like
when(database.getSomething()).thenReturn(something)
b. verify your presenter does what you want it to do with these objects like
verify(database).saveSomething()
(pseudo code)
Standard way to mock would be Mockito.
You can swap out real modules with fake modules in two ways: do it at compile time using flavors as recommended by google architecture samples or at runtime by creating an abstract method which injects the real thing in production code and fake dependencies in test code. In the second case your test has to subclass the class that you want to mock and build the component from scrach

Android Instrumentation Test with PoweMockito

I need to mock a static method on an android instrumentation test.
If i need to mock a static method, the test class needs to be #RunWith(PowerMockRunner.class) . But my instrumentation test are required to run with AndroidJUnit4.class .
Is it possible to have two runnable? or is there any other way i can use power mock to mock static methods? or any other options to mock static classes?
To handle cases like this since version 1.4 it's possible to bootstrap PowerMock using a JUnit Rule instead of the runner. Something looking like this:
#RunWith(AndroidJUnit4.class)
#PrepareForTest(X.class);
public class MyTest {
#Rule
PowerMockRule rule = new PowerMockRule();
// Tests goes here
...
}
But be aware that PowerMock is using byte-code manipulation which someone needs to convert to dalvikVM dex. And currently there are no tools to support that (https://groups.google.com/forum/#!topic/powermock/9kwPaWoZ_14 , https://stackoverflow.com/a/27956309/624706)

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)

Integrating Robolectric and Cucumber

I want to combine both Robolectric and Cucumber (JVM).
Currently I have two classes ActivityStepdefs where two step definitions for activity management are defined.
My second class is RoActivity Where for example an activity is created from it's class name, and where Robolectric will be used.
When I run RoActivityTest using RobolectricTestRunner the test in this class passes, but when I run RunCukesTest (class for running features as junit test) the code from RoActivity is not running as part of Robolectric, i.e. RunCukesTest search for features on my project and match it with a method inside ActivityStepdefs and finally this class will call a method from RoActivity
Is possible to run test with both junit both* runners?
I'm not sure but perhaps it's possible to do something like powermock, using junit rules.
In that case for which one should I have to define the rule?
*Cucumber and Robolectric
My small 5 cents.
Cucumber is mostly used for acceptance tests (correct me if you use it for unit testing) and Robolectric is mostly used for unit testing.
As for me, it is overkill to write cucumber during TDD. And Robolectric is still not android and I would run acceptance tests on real device or at least emulator.
I'am facing the same problem, after some google work, I got a solution:
#RunWith(ParameterizedRobolectricTestRunner::class)
#CucumberOptions( features = ["src/test/features/test.feature","src/test/features/others.feature"], plugin = ["pretty"])
class RunFeatures(val index: Int, val name:String) {
companion object {
#Parameters(name = "{1}")
#JvmStatic
fun features(): Collection<Array<Any>> {
val runner = Cucumber(RunFeatures::class.java)
Cucumber()
val children = runner.children
return children.mapIndexed{index, feature ->
arrayOf(index,feature.name)
}
}
}
#Test
fun runTest() {
val core = JUnitCore()
val feature = Cucumber(RunFeatures::class.java).children[index]!!
core.addListener(object: RunListener() {
override fun testFailure(failure: Failure?) {
super.testFailure(failure)
fail("$name failed:\n"+failure?.exception)
}
})
val runner = Request.runner(feature)
core.run(runner)
}
}
but seems not an pretty solution for me, can somebody help me out these problem:
must explicitly list all feature file path. but cannot use pattern such as *.feature
when failed cannot know which step failed.
parameter can only pass primitive type data,
I've get into cucumber source , but seems CucumberOptions inline Cucumber , I cannot pass it programmatically but can only use annotation .

Categories

Resources