How To Mock Getapplicationcontext
Solution 1:
Since the method getApplicationContext
is inside the class that you're extending it becomes somewhat problematic. There are a couple of problems to consider:
- You really can't mock a class that is under test, which is one of the many drawbacks with object inheritance (i.e. subclassing).
- The other problem is that
ApplicationContext
is a singleton, which makes it all more evil to test since you can't easily mock out a global state that is programmed to be irreplaceable.
What you can do in this situation is to prefer object composition over inheritance. So in order to make your Activity
testable you need to split up the logic a little. Lets say that your Activity
is called MyActivity
. It needs to be composed of a logic component (or class), lets name it MyActivityLogic
. Here is a simple class-diagram figure:
To solve the singleton problem, we let the logic be "injected" with an application context, so it can be tested with a mock. We then only need to test that the MyActivity
object has put the correct application context into MyActivityLogic
. How we basically solve both problems is through another layer of abstraction (paraphrased from Butler Lampson). The new layer we add in this case is the activity logic moved outside of the activity object.
For the sake of your example the classes need to look sort-of like this:
publicfinalclassMyActivityLogic {
private MyApp mMyApp;
publicMyActivityLogic(MyApp pMyApp) {
mMyApp = pMyApp;
}
public MyApp getMyApp() {
return mMyApp;
}
publicvoidonClick(View pView) {
getMyApp().setNewState();
}
}
publicfinalclassMyActivityextendsActivity {
// The activity logic is in mLogicprivatefinal MyActivityLogic mLogic;
// Logic is created in constructorpublicMyActivity() {
super();
mLogic = newMyActivityLogic(
(MyApp) getApplicationContext());
}
// Getter, you could make a setter as well, but I leave// that as an exercise for youpublic MyActivityLogic getMyActivityLogic() {
return mLogic;
}
// The method to be testedpublicvoidonClick(View pView) {
mLogic.onClick(pView);
}
// Surely you have other code here...
}
It should all look something like this:
To test MyActivityLogic
you will only need a simple jUnit TestCase
instead of the ActivityUnitTestCase
(since it isn't an Activity), and you can mock your application context using your mocking framework of choice (since handrolling your own mocks is a bit of a drag). Example uses Mockito:
MyActivityLogic mLogic; // The CUT, Component Under Test
MyApplication mMyApplication; // Will be mockedprotectedvoidsetUp() {
// Create the mock using mockito.
mMyApplication = mock(MyApplication.class);
// "Inject" the mock into the CUT
mLogic = new MyActivityLogic(mMyApplication);
}
publicvoidtestOnClickShouldSetNewStateOnAppContext() {
// Test composed of the three A's // ARRANGE: Most stuff is already done in setUp// ACT: Do the test by calling the logic
mLogic.onClick(null);
// ASSERT: Make sure the application.setNewState is called
verify(mMyApplication).setNewState();
}
To test the MyActivity
you use ActivityUnitTestCase
as usual, we only need to make sure that it creates a MyActivityLogic
with the correct ApplicationContext
. Sketchy test code example that does all this:
// ARRANGE:MyActivityvMyActivity= getActivity();
MyAppexpectedAppContext= vMyActivity.getApplicationContext();
// ACT: // No need to "act" much since MyActivityLogic object is created in the // constructor of the activityMyActivityLogicvLogic= vMyActivity.getMyActivityLogic();
// ASSERT: Make sure the same ApplicationContext singleton is inside// the MyActivityLogic objectMyAppactualAppContext= vLogic.getMyApp();
assertSame(expectedAppContext, actualAppContext);
Hope it all makes sense to you and helps you out.
Solution 2:
My problem was not exactly the same, but similar. Here is what worked for me.
Firstly, at a class level for the test
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)@PrepareForTest({CustomApp.class, AppUtils.class})
public classCustomClientTest {
@Mock
Context mockContext;
@Mock
MainActivity mainActivityMock;
then the setup()
@Before
public void setUp() throws Exception {
PowerMockito.mockStatic(CustomApp.class);
PowerMockito.mockStatic(AppUtils.class);
when(CustomApp.getAppContext()).thenReturn(mockContext);
when(webViewMock.getContext()).thenReturn(mainActivityMock);
}
and finally in the test itself
@Test
public void testShouldDoMyMethodRight() {
// true testsassertTrue(customClient.shouldDoMethod(webViewMock, Constants.HAPPY_PATH));
assertTrue(customClient.shouldOverrideUrlLoading(webViewMock, Constants.SPECIAL_PATH));
}
Post a Comment for "How To Mock Getapplicationcontext"