Testing ActiveAndroid with Robolectric - android

What can I do to get some test coverage on ActiveAndroid's ContentProvider in Robolectric? This simple test fails.
The model:
#Table(name = "Things")
public class Thing extends Model {
public Thing() {
super();
}
}
The test:
#RunWith(RobolectricTestRunner.class)
public class ContentProviderTest {
#Test
public void itShouldQuery() throws Exception {
new Thing().save();
ContentResolver cr = new MainActivity().getContentResolver();
assertNotNull(
cr.query(Uri.parse("content://org.example/things"),
null, null, null, null));
}
}
The resulting stack trace:
java.lang.NullPointerException: null
at com.activeandroid.Cache.getTableInfo(Unknown Source)
at com.activeandroid.Model.<init>(Unknown Source)
at org.example.Thing.<init>(Thing.java:9)
at org.example.ProviderTest.itShouldQuery(ProviderTest.java:25)
The application context should be ok. By default, Robolectric creates the application that appears in the manifest, which in this case is com.activeandroid.Application.
So, I'm puzzled why the tableInfo in Cache is not initialized. Normal application execution works fine.

To automatically scan ActiveAndroid Models automatically during maven unit tests requires a simple change to ModelInfo.scanForModel.
In that method, there is a "Robolectric fallback" which detects and scans paths containing "bin". This handles Model classes in Eclipse projects.
Maven compiles to target/classes. An additional check for "classes" in scan paths in ModelInfo does the trick.
Adding an ActiveAndroid pull request for this soon.

Related

Android :cannot unit test android functions receiving NPE

I have just started unit testing in android. I cannot unit test android internal method to notify that data is changed (notifyDataSetChanged)and I am getting null pointer exception on this point. I am using mockito and power moockito to mock different objects.
public void RecyclerView(String value,Bitmap image) {
RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainActivity.this);
recyclerView.setAdapter(adapter);// set adapter on recyclerview
adapter.notifyDataSetChanged();// Notify the adapter
}
My unit test is:
#RunWith(PowerMockRunner.class)
#PrepareForTest({MainActivity.class})
public class MainActivityTest {
#Mock
Bitmap bitmap;
#Mock
RecyclerView recycleview;
#InjectMocks
Activity activity;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mainactivity = new MainActivity();
}
#Test
public void PopulateRecyleViewTest() {
try {
final RecyclerViewAdapter abc = PowerMockito.mock(RecyclerViewAdapter.class);
PowerMockito.whenNew(RecyclerViewAdapter.class).withArguments(mainactivity).thenReturn(abc);
doNothing().when(abc).notifyDataSetChanged(); //do nothing getting exception here
mainactivity.recyclerView = recycleview;
mainactivity.PopulateRecyleView("", bitmap);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Is there any way to unit test this method so that notifydatasetchanged() do not give NPE(Null Pointer Exception)? I have read that power mockito is used to unit test final method but it does not seem to be unit testing notifyDataSetChanged which is final method. Any help would be appreciated.
Acc to Android documentation - "If your unit test has no dependencies or only has simple dependencies on Android, you should run your test on a local development machine"
Mockito/PowerMockito runs on your JVM.
#InjectMocks
Activity activity;
It is not straight forward to mock the activity that is why we have Instrumented Unit Tests. I see lot of problem's in the above test case. I think you should write instrumentation case in your case.
Use Mockito/PowerMockito where you have more of Java objects, like your presentation class (in MVP architecture) or any business logic.
Please checkout android documentation on testing here.
This will give you proper picture of how and what should be your approach for writing unit test cases in Android.
With Mockito 2.0 and above you can mock final classes and methods by adding a file. Check out this document: Mock the unmockable: opt-in mocking of final classes/methods
Basically it states that you have to create the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker then add the single line:
mock-maker-inline
After that you should be able to mock a final class or method. I had the same issue you had and this worked for me.

Unit testing Realm + Dagger 2 with Robolectric & Mockito

So I am working on this little project that uses Dagger 2 for dependency injection and Realm as a database.
I am unit testing it with Robolectric and Mockito (with Powermock). From previous research (and a lot of pain) I realised testing Realm is pretty laborious, but has been done in the past here.
Now, my project has a very similar setup and structure to the one linked above.
When I run my unit tests, all of them pass except for one that gives me a very cryptic message that looks as follows:
java.lang.NullPointerException
at org.robolectric.internal.ShadowExtractor.extract(ShadowExtractor.java:5)
at org.robolectric.Shadows.shadowOf(Shadows.java:1190)
at org.robolectric.shadows.CoreShadowsAdapter.getMainLooper(CoreShadowsAdapter.java:37)
at org.robolectric.util.ComponentController.<init>(ComponentController.java:31)
at org.robolectric.util.ComponentController.<init>(ComponentController.java:23)
at org.robolectric.util.ActivityController.<init>(ActivityController.java:40)
at org.robolectric.util.ActivityController.of(ActivityController.java:32)
at org.robolectric.Robolectric.buildActivity(Robolectric.java:82)
at org.robolectric.Robolectric.buildActivity(Robolectric.java:78)
at org.robolectric.Robolectric.setupActivity(Robolectric.java:86)
at uk.co.placona.tradesafe.view.EditActivityTest.ActivityShouldNotBeNull(EditActivityTest.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.powermock.modules.junit4.internal.impl
The line of code specified on the error above is:
activity = Robolectric.setupActivity(EditActivity.class);
The activity exists, and has a TradeRepository injected to it when it starts up.
The activity in question can be found here along with the rest of the code. I have been trying to debug this for about 3 days now with no success. Every other unit test I create works fine, except any unit test that is used by an Activity, which makes me think I'm probably missing something really obvious.
Would be happy to clarify any questions here. With many thanks!
static is evil, powermock is evil :).
I think you should get rid of your class Injector. You don't need it because you have only one CustomApplication object during the lifetime of your application.
You should modify your code as follow:
In CustomApplication.java, the application component is created, set in a field variable, and injected
private ApplicationComponent applicationComponent;
public void setup(){
getOrCreateApplicationComponent().inject(this);
databaseRealm.setup();
stethoDebug.setup(this);
}
public ApplicationComponent getOrCreateApplicationComponent() {
if (applicationComponent == null) {
applicationComponent = DaggerApplicationComponent.builder()
.applicationContextModule(new ApplicationContextModule(this))
.repositoryModule(new RepositoryModule())
.build();
}
return applicationComponent;
}
In the methods onCreate of CreateActivity, EditActivity, and MainActivity, Injector is replaced with
((CustomApplication) getApplication())
.getOrCreateApplicationComponent()
.inject(this);
In RepositoryModule we will use Dagger 2 to inject dependencies into constructors so we don't need to inject manually the Context and the DatabaseRealm
#Provides
#Singleton
public TradeRepository provideTradeRepository(DatabaseRealm databaseRealm) {
return new TradeRepositoryImpl(databaseRealm);
}
#Provides
#Singleton
public DatabaseRealm provideDatabaseRealm(Context context) {
return new DatabaseRealm(context);
}
then in DatabaseRealm we add a constructor with Context as parameter
Context mContext;
RealmConfiguration realmConfiguration;
public DatabaseRealm(Context context) {
mContext = context;
}
and same in TradeRepositoryImpl, a constructor with databaseRealm is added
DatabaseRealm databaseRealm;
public TradeRepositoryImpl(DatabaseRealm databaseRealm) {
this.databaseRealm = databaseRealm;
}
For RepositoryTestModule, we add databaseRealm as parameter:
#Provides
#Singleton
public TradeRepository provideTradeRepository(DatabaseRealm databaseRealm) {
return isMocked ? mock(TradeRepository.class) : new TradeRepositoryImpl(databaseRealm);
}
in your TestCustomApplication we override the getOrCreateApplicationComponent
#Override
public ApplicationComponent getOrCreateApplicationComponent() {
return DaggerApplicationComponentTest.builder()
.applicationContextModuleTest(new ApplicationContextModuleTest())
.repositoryModuleTest(new RepositoryModuleTest(false))
.build();
}
Now for each of your tests we run them with RobolectricGradleTestRunner and add TestCustomApplication.class as application tag
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21, application = TestCustomApplication.class)
when we need to inject dependencies into ours tests we will inject like this:
#Before
public void setupDagger() {
DaggerApplicationComponentTest.builder()
.applicationContextModuleTest(new ApplicationContextModuleTest())
.repositoryModuleTest(new RepositoryModuleTest(false))
.build().inject(this);
}
We still have a NullPointerException in our EditActivityTest because this line:
loadTrade(intent.getExtras().getString("ID"));
Either you check the intent is not null or you provide one in your test.
Looking to your test class, I don't see that you use any of Robolectric test runners.
You must use RobolectricGradleTestRunner or RobolectricTestRunner to trigger Robolecric functionality about loading manifest, parsing resources, creating main looper, etc.
If you don't use them, you probably can achieve it with own code in setup, but it is not the usual way, and I'm not sure that many people here can explain to you how to achieve it.
As well, Robolectric and PowerMock are modifying java ClassLoader both. That is why it is so hard (maybe impossible) to get them together working. So check #Steve answer how to modify your code to remove PowerMock necessity for your test.

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.

android: a newby question about Android Unit Testing?

I've got the basic scenario: a test project, in it - one test class, nothing less, nothing more. The code of the test class is this:
public class SManagerTest extends AndroidTestCase {
private SManager sm;
public SManagerTest(){
sm = SManager.getInstance(getContext());
}
#Test
public void trainTest(){
sm.go();
}
}
What's wrong? because I get this:
Test run failed: Test run incomplete. Expected 1 tests, received 0
Thanks!
As pointed out earlier use "test" with a small t and don't forget to run it as an "Android JUnit Test"
Agree with Christopher - start the method name with 'test' even though you're adding the #Test decorator. Also, add this class in the same folder as your tests and it will run all your tests:
public class AllTests extends TestSuite
{
public static Test suite()
{
return new TestSuiteBuilder(AllTests.class).includeAllPackagesUnderHere().build();
}
}

Categories

Resources