Firebase Analytics initialise for unit test (Mockito) in Android - android

I am running a unit test with
#RunWith(MockitoJUnitRunner.class)
I have used firebase Analytics to log events
MyApplication.getAnalytics().getInstance(appContext).logEvent(eventType, bundle)
and this in my Application class
public static FirebaseAnalytics getAnalytics() {
return FirebaseAnalytics.getInstance(appContext);
}
Now while running tests, I am getting NullPointerException. What will be the right way to initialize Analytics for my unit tests or just ignore them.
I am not getting the context in case I try to initialize it in my setup method of tests.

You can create a mock application class that extends your application class and then overrides getAnalytics with a stubbed value or mock object. Also you should make your getAnalytics method non-static as it's easier for testing and you can pass the reference via dependency injection or you can use a static reference to the application class (but that isn't very testable so I would choose the first option)
public class MockApplication extends MyApplication {
public FirebaseAnalytics getAnalytics() {
return mock(FirebaseAnalytics.class)
}
}
Then you can use the #Config annotation to configure your test runner like this
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.M, application = MockApplication.class)

Check this link out https://github.com/robolectric/robolectric/wiki/Using-PowerMock
Refactor yours like this:
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class)
#PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
#PrepareForTest(FirebaseAnalytics.class)
public class TestClass {
#Rule
public PowerMockRule rule = new PowerMockRule();
private FirebaseAnalytics firebase;
#Test
public void testMocking() {
firebase = PowerMockito.mock(FirebaseAnalytics.class);
Context context = PowerMockito.mock(Context.class);
PowerMockito.mockStatic(FirebaseAnalytics.class);
Mockito.when(FirebaseAnalytics.getInstance(context)).thenReturn(firebase);
}
}

Related

Getting null pointer object on hilt inject object in activity when run test cases

Please check below my Module class in which I have defined my object which need to be inject using Hilt
NVModule.kt
#Module
#InstallIn(SingletonComponent::class)
class NVModule {
#Provides
#Named("ProfileHelper")
fun abprovideProfileHelper(): ProfileHelper {
return ProfileHelper(AppController.getInstance())
}
}
And now please check my Interface by which i have used the EntryPoint to access my dependency injection outside the Activity/Fragment like Helper class.
#EntryPoint
#InstallIn(SingletonComponent.class)
public interface CommonHiltInterface {
#Named("ProfileHelper")
public ProfileHelper provideProfileHelper();
}
}
Now please check the my Activity class on which i have used the dependency injection like below and it is working fine here. Means getting dependency injection properly
public class HomeActivity extends BaseActivity{
private ActivityHomescreenBinding
activityHomescreenBinding;
private Activity context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
activityHomescreenBinding =
DataBindingUtil.inflate(getLayoutInflater(),
R.layout.activity_homescreen, null, false);
setContentView(activityHomescreenBinding.getRoot());
CommonHiltInterface commonHiltInterface = EntryPointAccessors.fromApplication(context, CommonHiltInterface.class);
commonHiltInterface.provideProfileHelper().setData();
}
}
But in case of the Test cases , dependency injection getting NullPointerException . I am using the Robolectric for the test cases. Please check my below lines of code for the RobolectricTest case.
#HiltAndroidTest
#RunWith(RobolectricTestRunner.class)
#Config(application = HiltTestApplication.class,
sdk = Build.VERSION_CODES.N, manifest = Config.NONE)
public class HomeActivityTest {
#Rule
public HiltAndroidRule hiltRule = new
HiltAndroidRule(this);
#Before
public void setUp() throws Exception {
shadowOf(Looper.getMainLooper()).idle();
hiltRule.inject();
activity =
Robolectric.buildActivity(HomeActivity.class).
create().resume().get();
}
}
Note :- 1). I have also use #HiltAndroidApp() for application class.and using 2.36 version for hilt dependency
2). My dependency injection working fine for the Java classes like Activity/Fagment and Helper classes , But not working in test cases.
Please check my dependency for Hilt are as follow
testImplementation 'com.google.dagger:hilt-android-testing:2.36'
kaptTest 'com.google.dagger:hilt-android-compiler:2.36'
testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.36'
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.36'
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.36'
Application runs successfully but in case of Test case I am getting the null pointer exception below lines of code in Activity (HomeActivity).
CommonHiltInterface commonHiltInterface = EntryPointAccessors.fromApplication(context, CommonHiltInterface.class);
commonHiltInterface.provideProfileHelper().setData();

Robolectric Testing activity with dagger2

I have a activity that extend DaggerAppCompatActivity to enable injections on it using new dagger android injection tools
I'm trying test this activity using Robolectric but the test throws
java.lang.RuntimeException: android.app.Application does not implement dagger.android.HasActivityInjector
How to disable dagger 2 injection to test the activity as normal activity
the test code
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class)
public class RegisterActivityTest {
AppCompatActivity activity;
#Before
public void setupActivity() {
activity = Robolectric.setupActivity(RegisterActivity.class);
}
#Test
public void clickingNewAccountText_MakeNewAccountShouldBeVisible() {
TextView registerNewAccountTextView = activity.findViewById(R.id.register_sign_up_textView);
registerNewAccountTextView.performClick();
Button registerNewAccountButton = activity.findViewById(R.id.register_sign_up_button);
assertThat(registerNewAccountButton.getVisibility(), is(View.VISIBLE));
}
}
any idea how to solve this problem
I was having a similar problem earlier. The error you posted appears to indicate that your activity is calling AndroidInjection.inject(this).
Are you defining your own Application class that implements HasActivityInjector?
According to Robolectric's documentation
Robolectric will attempt to create an instance of your Application
class as specified in the manifest
In my case what worked was configuring the unit tests to include the Android resources:
testOptions{
unitTests{
includeAndroidResources true
}
}
in my module build.gradle, under the android{} section.

DaggerMock library -how does it override module?

The DaggerMock library, is used to override dagger modules with fake implementation. Lets take a look at one robolectric topic that is confusing me:
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {
#Rule public final DaggerMockRule<MyComponent> mockitoRule = new DaggerMockRule<>(MyComponent.class, new MyModule())
.set(new DaggerMockRule.ComponentSetter<MyComponent>() {
#Override public void setComponent(MyComponent component) {
((App) RuntimeEnvironment.application).setComponent(component);
}
});
#Mock RestService restService;
#Mock MyPrinter myPrinter;
#Test
public void testCreateActivity() {
when(restService.doSomething()).thenReturn("abc");
Robolectric.setupActivity(MainActivity.class);
verify(myPrinter).print("ABC");
}
}
So i want to know, with this Rule what exactly is happening ? I can see that RestService was being provided by MyModule but is now being replaced with a mock. But in the examples i don't see a #Inject anywhere so i'm confused how the module was even used in the first place to provide any dependencies ?
I am the author of DaggerMock, thanks for trying it!
The implementation is a bit complicated, the rule create a dynamic subclass of the module (using mockito) and override the provides methods. The rule scans the test fields so it return a field when the module has a method that returns the same type.
The final result is very similar to Mockito InjectMocks annotation. You can take a look at the implementation on github, the core class that override the module is this: https://github.com/fabioCollini/DaggerMock/blob/master/lib/src/main/java/it/cosenonjaviste/daggermock/MockOverrider.java
I release this lib just a week ago, any feedback is welcome!

Using mocked objects

I've just started unit testing on Android with Mockito - how do you get the class that you are testing on to use the mocked class/object instead of the regular class/object?
You can use #InjectMocks for the class you writing the test.
#InjectMocks
private EmployManager manager;
Then you can use #Mock for the class you are mocking. This will be the dependency class.
#Mock
private EmployService service;
Then write a setup method to make things available for your tests.
#Before
public void setup() throws Exception {
manager = new EmployManager();
service = mock(EmployService.class);
manager.setEmployService(service);
MockitoAnnotations.initMocks(this);
}
Then write your test.
#Test
public void testSaveEmploy() throws Exception {
Employ employ = new Employ("u1");
manager.saveEmploy(employ);
// Verify if saveEmploy was invoked on service with given 'Employ'
// object.
verify(service).saveEmploy(employ);
// Verify with Argument Matcher
verify(service).saveEmploy(Mockito.any(Employ.class));
}
By injecting the dependency:
public class ClassUnderTest
private Dependency dependency;
public ClassUnderTest(Dependency dependency) {
this.dependency = dependency;
}
// ...
}
...
Dependency mockDependency = mock(Dependency.class);
ClassUnderTest c = new ClassUnderTest(mockDependency);
You can also use a setter to inject the dependency, or even inject private fields directly using the #Mock and #InjectMocks annotations (read the javadoc for a detailed explanation of how they work).

Get context of test project in Android junit test case

Does anyone know how can you get the context of the Test project in Android junit test case (extends AndroidTestCase).
Note: The test is NOT instrumentation test.
Note 2: I need the context of the test project, not the context of the actual application that is tested.
I need this to load some files from assets from the test project.
There's new approach with Android Testing Support Library (currently androidx.test:runner:1.1.1). Kotlin updated example:
class ExampleInstrumentedTest {
lateinit var instrumentationContext: Context
#Before
fun setup() {
instrumentationContext = InstrumentationRegistry.getInstrumentation().context
}
#Test
fun someTest() {
TODO()
}
}
If you want also app context run:
InstrumentationRegistry.getInstrumentation().targetContext
Full running example: https://github.com/fada21/AndroidTestContextExample
Look here: What's the difference between getTargetContext() and getContext (on InstrumentationRegistry)?
After some research the only working solution seems to be the one yorkw pointed out already. You'd have to extend InstrumentationTestCase and then you can access your test application's context using getInstrumentation().getContext() - here is a brief code snippet using the above suggestions:
public class PrintoutPullParserTest extends InstrumentationTestCase {
public void testParsing() throws Exception {
PrintoutPullParser parser = new PrintoutPullParser();
parser.parse(getInstrumentation().getContext().getResources().getXml(R.xml.printer_configuration));
}
}
As you can read in the AndroidTestCase source code, the getTestContext() method is hidden.
/**
* #hide
*/
public Context getTestContext() {
return mTestContext;
}
You can bypass the #hide annotation using reflection.
Just add the following method in your AndroidTestCase :
/**
* #return The {#link Context} of the test project.
*/
private Context getTestContext()
{
try
{
Method getTestContext = ServiceTestCase.class.getMethod("getTestContext");
return (Context) getTestContext.invoke(this);
}
catch (final Exception exception)
{
exception.printStackTrace();
return null;
}
}
Then call getTestContext() any time you want. :)
If you want to get the context with Kotlin and Mockito, you can do it in the following way:
val context = mock(Context::class.java)
import androidx.test.core.app.ApplicationProvider;
private Context context = ApplicationProvider.getApplicationContext();
#RunWith(AndroidJUnit4.class) let you use Android Context
/**
* Instrumented test, which will execute on an Android device.
*
* #see Testing documentation
*/
#RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
#Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.android.systemui", appContext.getPackageName());
}
}
You can even run it on main thread using runOnMainSync. Here is the complete solution:
#RunWith(AndroidJUnit4::class)
class AwesomeViewModelTest {
#Test
fun testHandler() {
getInstrumentation().runOnMainSync(Runnable {
val context = InstrumentationRegistry.getInstrumentation().targetContext
// Here you can call methods which have Handler
})
}
}
Update: AndroidTestCase This class was deprecated in API level 24.
Use InstrumentationRegistry instead. New tests should be written using the Android Testing Support Library. Link to announcement
You should extend from AndroidTestCase instead of TestCase.
AndroidTestCase Class Overview
Extend this if you need to access Resources or other things that depend on Activity Context.
AndroidTestCase - Android Developers
This is to correct way to get the Context. Other methods are already deprecated
import androidx.test.platform.app.InstrumentationRegistry
InstrumentationRegistry.getInstrumentation().context
The other answers are outdated. Right now every time that you extend AndroidTestCase, there is mContext Context object that you can use.
For those encountering these problems while creating automated tests, you've gotta do this :
Context instrumentationContext;
#Before
public void method() {
instrumentationContext = InstrumentationRegistry.getInstrumentation().getContext();
MultiDex.install(instrumentationContext);
}
Add Mocito Library
testImplementation 'junit:junit:4.13.2'
testImplementation 'androidx.test:core:1.4.0'
testImplementation 'org.mockito:mockito-core:3.10.0'
Add Annoatation call #Mock where ever need for example for Context
#RunWith(MockitoJUnitRunner::class)
class EmailValidatorTest {
#Mock
private lateinit var context: Context
lateinit var utils:Utils
#Before
fun launch()
{
utils=Utils(context)
}
#Test
fun emailValidator_NullEmail_ReturnsFalse() {
assertFalse(utils.isValidEmail(null))
}
}
For Kotlin unit test with #RunWith(AndroidJUnit4::class)
Add this dependency for kotlin test
implementation 'androidx.test:core-ktx:1.5.0'
In the test class access context using the below snippet.
private val context = ApplicationProvider.getApplicationContext<Context>()

Categories

Resources