Android Testing custom views with mockito - android

I have a Customview class and I want to write a simple test for it. At first I want to check if the LayoutParams are set.
CustomView Class
public class CustomView extends FrameLayout {
public CustomView(#NonNull Context context) {
super(context);
initFrameLayout();
}
public void initFrameLayout() {
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
this.setLayoutParams(layoutParams);
}
}
CustomViewTest class
public class CustomViewTest {
#Test
public void viewInitializedCorrectly() {
Context context = mock(Context.class);
CustomView customView = new CustomView(context);
int expectedViewWidth = FrameLayout.LayoutParams.MATCH_PARENT;
assertEquals(expectedViewWidth, customView.getLayoutParams().width);
}
The test fails with an NullPointerException. I inspected the method with the debugger and I note, that the FrameLayout Object exists but without parameter. Should I mock the CustomView.class too?

There are a number of different types of tests in Android. Local Unit Tests run in your IDE on your laptop or desktop and Instrumented Unit Tests run on a device.
Local Unit Tests do not ordinarily have access to Android SDK classes like FrameLayout. Instead, you get stubbed out versions of these classes that return null. This explains the NullPointerException
For getting around the error, you could mock FrameLayout manually or use something like Robolectric which is a framework that provides test doubles called "shadows" of Android classes like FrameLayout.
However, in general custom views are poorly suited for unit testing since they cannot have mocks injected easily (since they are inflated by the OS from XML attributes) and the tests can often degenerate into a reverse implementation of the class. If the custom view does require testing beyond "it looks right" a better option may be to write Espresso automated UI tests which are more suited for that sort of thing.

Related

Android MVP. Testing View (Instrumental tests or unit test using Robolectric)

Have the following doubts about testing View (Activity, Fragment). The first approach is to test view using robolectric with mocking Android components like the following code do:
private void setUp() {
fragment = new CustomFragment();
activity = Robolectric.setupActivity(MainActivity.class);
}
#Test
public void testOnCreateView() {
fragment.onCreateView(LayoutInflater.from(activity), (ViewGroup) activity.findViewById(R.id.container), null);
verify(repoInfoPresenter).onCreateView(null);
}
Don't like this approach, cause this tests executing takes long time. In my opinion unit tests should be executed on each merge request, so they shouldn't be as slow as tests like that.
Another variant is to write Instrumentation tests using Espresso. I know that it's already takes long time and real device, but I don't think that this test should executed as often as tests running on jvm.
Or maybe there is magical variant to mock(make stubs) for fragments or Activities which I don't know about?

MVP pattern. Is it a good practice to inject all presenter's dependencies in a constructor of the presenter?

I want my code to be testable and flexible and I cannot make a choice whether I need to pass all dependencies explicitly to a constructor of a presenter or it's better to pass only View interface to a constructor and inject all dependencies in it.
It is a desirable pattern.
Every time you have access to object constructor, you should inject dependencies in constructor or other methods. #Inject annotation is indended mainly to be used inside object, that are not created by you.
When injecting all dependencies in constructor, during tests you pass all your dependencies to your model during initialisation. Therefore every test might contain different dependencies and different instance of created class. That is also the aim of unit tests - provide a sandbox for every test.
It is also easier to mock dependencies with Mockito.
Remember, that in unit tests, you don't have dependency any framework configured. A unit test usually contains created model and nothing more. Everything must be created by you (or mocked by framework)
Here is a sample unit test that proves the statement above:
#RunWith(MockitoJUnitRunner.class)
public class GetAreasUseCaseTest {
#Mock ApiManager mApiManager;
#Mock DatabaseManager mDatabaseManager;
private GetAreasUseCase mGetAreasUseCase;
#Rule
public final RxSchedulersOverrideRule mOverrideSchedulersRule = new RxSchedulersOverrideRule();
#Before
public void setUp() {
mGetAreasUseCase = new GetAreasUseCase(mApiManager,
mDatabaseManager);
doReturn(Observable.empty())
.when(mDatabaseManager)
.insertAreas(any(Area.class));
}
#Test
public void testGetAreasUseCaseApiInteraction() throws Exception {
TestSubscriber<List<Area>> testSubscriber = new TestSubscriber<>();
setCorrectApiResponse();
boolean input = true;
Observable testedObservable = mGetAreasUseCase.build(input);
testedObservable.subscribe(testSubscriber);
verify(mApiManager).getAreas(anyLong());
}
}
As you can see the structure of the test is very clear. It is well known, what is mocked and which object is tested. You have the control of the behaviour of dependencies.
If you plan to do only instrumentation tests, then you are provided with the ApplicationContext in tests and Dagger is properly initialised. There is no difference here. Of course you might still emulate Module's and Component's behaviour and provide custom object's Mocks instead of real classes (Example)
its not a bad practice but its hard to change the code in later changes. what if you want to add a new dependency for presenter ? then you have to change almost everything in your code.
what you are going to do actually is called Dependency Injection and the best practice is to do Dependency Injection, is using libraries like Dagger.
i think Dagger is the most powerful Dependency Injection library up to now. you will write some methods for providing your dependencies and Dagger will provide them for you whenever you want them using a #inject Annotation. for a complete instruction see this link:
http://www.vogella.com/tutorials/Dagger/article.html

Inheritance of TestCases on Android

I was wondering if it was good practice to subclass the test cases on Android. I mean, I need to test a lot of Parcelable objects and I could create a class like GenerericParcelableAndroidTestCase to test all these objects.
I also have a problem implementing it, I have something like this:
public class GenericParcelableTest extends AndroidTestCase {
private Parcelable p = null;
GenericParcelableTest(Parcelable p) {
this.p = p;
}
public void testDescribeContents() throws Exception {
assertEquals(0, p.describeContents());
}
}
And that:
public class AttachmentTest extends GenericParcelableTest {
public AttachmentTest() {
super(new Attachment());
}
}
Attachment implements Parcelable of course.
It returns me this error:
junit.framework.AssertionFailedError: Class GenericParcelableTest has no public constructor TestCase(String name) or TestCase()
I mean, I know that I created no empty constructor but why would I need one?
And generally, is there some known issues with this approach? If not why is there very few article on this topic on the internet (and actually some say even that it's not a good idea).
I have this conversation quite often when introducing new team members to unit testing. The way I explain it is by stating that your tests are first class citizens of your code base (no pun intended), they are susceptible to the same technical debt as any other part of your code base and have equivalent (maybe more?!) importance as that of the runtime code.
With this mindset, the questions begins to answer itself; if it makes sense from an OO perspective to use inheritance (i.e. your subclass is a insert name of test superclass) then subclass away. However, like any abuse of inheritance ever, be careful...the minute you add a test case that doesn't rely upon that superclass behaviour you may have a code smell.
In this scenario, it's likely (perhaps 90% of the time?) it is a separation of concern issue within the code being placed under test, i.e. the "unit" under test isn't actually (one) unit but has combinatorial behaviour. Refactoring that code to do one thing would be a good way of allowing your super-class test case to live on. However, watch this super class test case like a hawk...the minute you see booleans being added to signatures to "allow that similar but not the same" test case to run under your once unpolluted super class then you have a problem, a tech debt problem that is no different to your runtime code.
At last check AndroidTestCase depends on an Activity context so it's likely best described as an integration test which tend to regularly have boilerplate super-class test behaviour. In this case, try to narrow the focus of your superclass to the use case under test...i.e. extends LoginUseCase or extends LoginScenario to better "bucket" those subclasses in the first instance. This will help guide would be extenders as to whether they should be using it for their non-login scenario. Hopefully, conversation will ensue and tech debt accumulation be avoided!
Regarding your error, in JUnit3 do what #Allen recommends, if moving to JUnit4 with something like Robolectric then explore using Rules as well as #BeforeClass.
Personal note
I have only felt the need to write test super classes for pseudo-unit tests that mock an API end point (akin to MockWebServer if you are familiar with that product) and DAO integration tests whereby an in-memory db is started and torn down over the lifecycle of each test (warning - slow (but useful) tests!)
junit.framework.AssertionFailedError: Class GenericParcelableTest has no public constructor TestCase(String name) or TestCase()
You get this error because JUnit needs to be able to construct an instance of your test class. It only knows how to do this using no-arg, or single string constructors.
Instead of performing initialization in your constructor, you should put it in the setUp() method. This will let you use the default constructor while still initializing the object before the test method is called.

Android JUnit4 Testing - Where to get Context from?

I have to build an app with sqlite usage. Now I want to write my unit tests. These unit tests should test my class SQLiteBridge. SQLiteBridge provides DAOs for every child class of Model.
Now I got the problem that I need a context to create my SQLiteBridge. SQLiteBridge creates and handles a SQLite database on the system..
Where to get the Context-Object from?
My setup is like here (so I'm using Junit4 [thanks god]):
http://tools.android.com/tech-docs/unit-testing-support
EDIT: I hope there is a way like the old AndroidTestCase to extend without losing Junit4. :)
As described here: https://code.google.com/p/android-test-kit/wiki/AndroidJUnitRunnerUserGuide
Use the InstrumentationRegistry to obtain the context.
However if you call InstrumentationRegistry.getContext() directly you may get an exception opening your database. I believe this is because the context returned by getContext() points to the instrumentation's context rather than that of your application / unit test. Instead use InstrumentationRegistry.getInstrumentation().getTargetContext()
For example:
#RunWith(AndroidJUnit4.class)
public class SqliteTest {
Context mMockContext;
#Before
public void setUp() {
mMockContext = new RenamingDelegatingContext(InstrumentationRegistry.getTargetContext(), "test_");
}
}
The RenamingDelegatingContext simply prefixes the file/database names with test_ to prevent you from overwriting data that you may have in the same simulator.
jUnit 4 (and perhaps other versions of jUnit) and androidx use:
ApplicationProvider.getApplicationContext();
See: Android Documentation

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.

Categories

Resources