Robolectric Application instance is always null - android

I tried running robolectric library with versions 3.3.2 and 3.4-rc3. My code internally tries to fetch a singleton instance of the application. But when my test runs, the application instance retrieved is always null. It works fine with robolectric 3.1.2. I wanted to upgrade as I wanted to use powermocks in the test. There were similar questions in stackoverflow. But it did not help me. Please advise. Following is my test code. I tried running with PowerMockRunnerDelegator as shown here. Also, tried with the normal RobolectricTestRunner as the Runner. Both the approaches did not work.
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21, application =
TestApplication.class)
#PowerMockIgnore({ "org.mockito.*", "org.robolectric.*",
"android.*"})
#PrepareForTest({MockActor.class})
public class MyTest {
#Mock
private MockActor mockActorInstance;
#Rule
public PowerMockRule rule = new PowerMockRule();
#Before
public void setup() throws Exception {
initMocks(this);
PowerMockito.mockStatic(MockActor.class);
whenNew(MockActor.class).withAnyArguments()
.thenReturn(mockActorInstance);
}
#Test
public void shouldTriggerCall() {
MyFragment myFragment = new MyFragment();
startFragment(myFragment);
myFragment.doAction();
verify(mockActorInstance).invoke();
}
}

You can implement a custom ShadowApplication and stub your static getApplication method there. Hopefully this https://medium.com/#chuangx/get-around-with-app-getapplication-in-robolectric-4b88d559fca5 helps

Related

Robolectric 3.8 with MVP and mocking setupActivity()

I use Android MVP architecture and I want to test my View. I have the following code in my project:
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {
#Test
public void testDisplay() throws Exception {
MainActivity activity = Robolectric.setupActivity(MainActivity.class);
final Menu menu = Shadows.shadowOf(activity).getOptionsMenu();
assertEquals("Log", menu.findItem(R.id.menu_login).getTitle().toString());
}
}
However, I get ExceptionInIntializationError for some 3rd party library throws it in the Activity's onResume() step which is invoked by Robolectric.
Moreover, in Activity's onResume() the Presenter's method is called which makes a network request.
My questions are:
How to mock 3rd party intialization step? I read about Shadows, but I don't know how to use them in this particular scenario. I should mock stuff during setupActivity(...) call.
How to mock the network call? In my MVP code, the presenter is created in onCreate() method. Should I switch to direct dependency injection pattern and rebuild the architecture? But this approach would conflict with Robolectric.setupActivity(MainActivity.class); as I need some kind of hook to inject stuff in onCreate() or onResume() during Robolectric calls.
Ok, I came up with the following solution. It is strange that I have never seen similar examples on their page or in articles. I have used Mockito along with Robolectric.
Firstly, I have added setPresenter method to Activity(MVP's View) to insert custom-mock Presenter into View
#VisibleForTesting
public void setPresenter(P presenter) {
this.presenter = presenter;
}
In my code, Presenter is created during onCreate() method, so I have added null-check in order to inject Presenter before onCreate():
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (presenter == null)
presenter = providePresenter(App.appComponentFrom(this));
presenter.create();
}
In Robolectric framework I have found buildActivity() method which just invokes Activity's constructor (no lifecycle methods!).
Then after this call I injected my mock-Presenter.
The last step is to call activityController.setup().get() as is does the same job as setupActivity(), which is calling lifecycle methods: create(), resume()
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {
#Mock
CheckoutContract.Presenter presenter;
private ShadowActivity mainActivity;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
ActivityController<MainActivity> activityController = Robolectric.buildActivity(MainActivity.class);
activityController.get().setPresenter(presenter);
mainActivity = Shadows.shadowOf(activityController.setup().get());
}
#Test
public void checkText() throws Exception {
assertEquals(RuntimeEnvironment.application.getString(R.string.activityMain_title),
((Toolbar)checkoutActivity.findViewById(R.id.vToolbar)).getTitle());
}
}
Voila! In my test I got Context Resources from RuntimeEnvironment and it have passed!

Robolectric not calling Application.onCreate()

I am very much new to Robolectric. I am facing these issues after migrating from Robolectric v3.0 to v3.4.1.
Robolectric is not loading my AppController class which extends Application
I am getting class cast exception on casting to RuntimeEnvironment.application to my AppController, which shouldn't be the case as my AppController extends Application and was working fine before migrating to latest version.
Please find the code below for my test
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, manifest = "app/src/main/AndroidManifest.xml", sdk = 21)
public class FragmentTest {
private ExampleFragment fragment = SupportFragmentController.of(new ExampleFragment()).create().get();
private Context mContext;
#Before
public void setUp() throws Exception {
// RuntimeEnvironment.application.onCreate();
mContext =RuntimeEnvironment.application ;
}
#Test
public void testFragmentInstantiation() {
ExampleFragment.mAppController = (AppController)mContext;
}
To reproduce - run any test.
Robolectric version is 3.4.1.
The main advice is to do not put manifest file location in the #Config section unless you know what you're doing. So please change your test annotations to:
#Config(constants = BuildConfig.class, sdk = 21)
Also, I don't know if it is by purpose but you could use much newer android sdk in your test with the newest version of Robolectric

Android Unit Testing: Cucumber-jvm + Android Instrumentation

Using: Cucumber-JVM with Android Instrumentation + Espresso).
Reference Github link: https://github.com/mfellner/cucumber-android for this. The simple sample works fine.
Problem with cucumber-jvm + android instrumentation:
But in the sample in link, it uses ActivityInstrumentationTestCase2 which is deprecated. I would like to use #Rule - ActivityTestRule class as said by Google.
Here my question is:
For using cucumber-jvm, I am using the CucumberInstrumentationCore instead of
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner".
So Android junit annotations like #Rule for ActivityTestRule is not parsed by CucumberInstrumentation. So Is it possible to overcome this problem?
Then is my decision to use cucumber-jvm + android instrumentation has to be reverted back. My question is not only for the deprecated class but globally is it good idea to go for cucumber-jvm + android instrumentation, as it can't use instrumentation features because of annotation parsing.
Your runner should inherit from Android JUnitRunner:
public class Instrumentation extends AndroidJUnitRunner {
private final CucumberInstrumentationCore instrumentationCore = new CucumberInstrumentationCore(this);
#Override
public void onCreate(final Bundle bundle) {
instrumentationCore.create(bundle);
super.onCreate(bundle);
}
#Override
public void onStart() {
waitForIdleSync();
instrumentationCore.start();
}
Pay attention to the super class been initialized at the end of onCreate.
Then, edit your defaultConfig in your build.grade file:
defaultConfig {
applicationId "your.package.name"
testApplicationId "your.steps.package"
testInstrumentationRunner "your.package.Instrumentation"
}
And finally, the steps definition class, which inherited from ActivityInstrumentationTestCase2 should look like:
public class BaseStepDefinitions {
public static final String TAG = BaseStepDefinitions.class.getSimpleName();
#Rule
public ActivityTestRule<StartupActivity> mActivityRule = new ActivityTestRule<>(StartupActivity.class);
#Before
public void setUp() throws Exception {
mActivityRule.launchActivity(null);
mActivityRule.getActivity();
}
/**
* All the clean up of application's data and state after each scenario must happen here
*/
#After
public void tearDown() throws Exception {
}
#When("^I login with \"([^\"]*)\" and \"([^\"]*)\"$")
public void i_login_with_and(String user, String password) throws Throwable {
// Login...
}
The setUp function runs before each scenario, and launching the activity.
Globally, if it serves your needs I don't see any problem using it like so, both Cucumber annotations and the JUnit annotations can be parsed in this way.
I've created a sample project: github.com/Clutcha/EspressoCucumber

Android - How to UnitTest a Logging class with mockito

I have written a class to manage logging within an android application project.
The LogManager is basically a wrapper for android.util.log
It handles logging to a file, if the application crashes, and standard debug logging.
I would like to unit test the class using JUnit.
I have tried the following but it does not seem to produce the results I would expect after reading the examples:
LogManager.class (This is a simplified version of the class I have used, for demonstration purposes)
public class LogManager implements ILogManager
{
public void log(String tag, String message)
{
Log.e(tag, message);
}
}
And here is my test class
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
#PrepareForTest({Log.class, LogManager.class})
public class LogManagerUnitTest
{
#Test
public void testLogConsoleInfo()
{
PowerMockito.mockStatic(Log.class);
LogManager.getInstance().log(LogLevel.INFO, "test", "test");
PowerMockito.verifyStatic(Mockito.times(1));
Log.e(anyString(), anyString());
}
}
My problem is that this passes no matter what I put.
E.g: if I instead replace the last call with Log.wtf(...) it still passes. I would have assumed that it should fail since Log.wtf was not called in the static class Log?
So my question is, why isn't this approach working as expected and what would be the correct way to do it?
I started a fresh project and was able to get it to fail tests and succeed appropriately using the following, so I'm assuming the runwith was the culprit:
#RunWith(PowerMockRunner.class)
#PrepareForTest(android.util.Log.class)
public class LoggerUnitTest {
#Test
public void testLog() throws Exception
{
PowerMockito.mockStatic(Log.class); // when(Log.e(anyString(), anyString())).thenReturn(1);
Logger.log("test", "test");
PowerMockito.verifyStatic(times(1));
Log.e(anyString(), anyString());
} }
For the RobolectricGradleTestRunner, the following incantation would have exposed your logging:
ShadowLog.stream = System.out
Robolectric does not print the Android system logging by default.
It's also worth noting that the RobolectricGradleTestRunner has been deprecated in favor of the fully operational RobolectricTestRunner (The above assignment is still effective)

Can't get JUnit tests to fail in Android Studio

I'm trying out Android development, but haven't come too far because I'm unable to get a test case to fail.
I have the following test case in the androidTest folder:
package com.example.aaronf.myapplication;
import android.test.*;
public class ToDoListTest extends AndroidTestCase {
private void newToDoListHasNoItems() {
assertEquals(new ToDoList().length, 0);
}
private void addingToDoGivesLengthOfOne() {
ToDoList toDoList = new ToDoList();
toDoList.add(new ToDo());
assertEquals(toDoList.length, 1);
}
public void runTests() {
newToDoListHasNoItems();
addingToDoGivesLengthOfOne();
}
public ToDoListTest() {
super();
runTests();
}
}
The ToDoList class looks like:
package com.example.aaronf.myapplication;
public class ToDoList {
public int length = 0;
public void add(ToDo toDo) {
}
}
It seems like it should fail on addingToDoGivesLengthOfOne(), but I get a green bar.
EDIT
I should add that adding #Test annotations to the methods generates a symbol not found error.
EDIT
I visited the suggested post My Junit test doesn't run. However, there is a difference with my problem. My methods used to have the test prefix, but this didn't affect the outcome. Also, the #Test annotation, as I mentioned before, is flagged with an error: "Cannot resolve symbol Test".
The problem was that my Test Artifact was set to Android Instrumentation Tests instead of Unit Tests. Since my unit tests were being added to the Android Instrumentation group, the unit testing stuff wasn't being recognized.

Categories

Resources