I have some ButtonCreator.class that create Android UI element via models. Have any ideas how to test it?
public class ButtonCreator {
Button createWidget(ButtonComponent buttonComponent, Context context) {
Button button = new Button(context);
button.setText(buttonComponent.getText());
return button;
}
}
I wrote a test:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Button.class, Context.class, ButtonCreator.class})
public class CreaterButtonTest {
#Mock
Context mContext;
#Mock
Button mButton;
#Test
public void createWidget() {
PowerMockito.whenNew(Button.class).withAnyArguments(). thenReturn(mButton);
ButtonCreator buttonCreator = new ButtonCreator();
ButtonComponent buttonComponent = new ButtonComponent();
buttonComponent.setText("someText");
Button buttonActual = buttonCreator.createWidget(buttonComponent, mContext);
assertThat(buttonActual.getText(), is("someText"));
}
}
But in buttonActual.getText() I have a null. Help it solve please.
Important thing to understand about mocks, is that they will never function as your original object, unless you specify the behavior.
In your case:
The Mocked button will receive the call of setText(), but because it's a Mocked object, it does not have any internal state to store the text string, so it will correctly return null.
You can specify the behavior to return a string with something like:
when(mButton.getText()).thenReturn("mytext");
But this is not so optional, because in the end you are testing how the mock framework is working.
In these cases, you should use the verify method. Every mock object records all method calls during it's lifetime.
verify(mButton, times(1)).setText("someText"));
With this you are making sure that the exact method calls are happening.
Related
I am new to Mockito and trying to understand how to use doAnswer in order to test a void method.
Here's my class with the onDestroy method to test:
public class TPresenter implements TContract.Presenter {
private CompositeSubscription viewSubscription;
//.......
#Override public void onCreate(.......) {
this.viewSubscription = new CompositeSubscription();
//.......
}
#Override public void onDestroy() {
if(viewSubscription != null && !viewSubscription.isUnsubscribed()) {
viewSubscription.unsubscribe();
}
}
Now I want to write a test for onDestroy() namely to verify that after executing onDestroy the subscription is unsubscribed. I found several examples to use doAnswer for testing void methods, for example here, and also here but I do not understand them.
Please show how to test the method onDestroy.
The normal way how you could test your onDestroy() would be based on viewSubscription being a mocked object. And then you would do something like:
#Test
public testOnDestroyWithoutUnsubscribe() {
when(mockedSubscription.isUnsubscribed()).thenReturn(false);
//... trigger onDestroy()
verifyNoMoreInteractions(mockedSubscription);
}
#Test
public testOnDestroyWithUnsubscribe() {
when(mockedSubscription.isUnsubscribed()).thenReturn(true);
//... trigger onDestroy()
verify
verify(mockedSubscription, times(1)).unsubscribe();
}
In other words: you create a mocked object, and you configure it to take both paths that are possible. Then you verify that the expected actions took place (or not, that is what the first test case does: ensure you do not unsubscribe).
Of course, you can't test the "subscription object is null" case (besides making it null, and ensuring that no NPE gets thrown when triggering the onDestroy()!
Given the comment by the OP: one doesn't necessarily have to use mocking here. But when you want to test a void method, your options are pretty limited. You have to observe side effects somehow!
If you can get a non-mocked viewSubscription instance to do that, fine, then do that. But if not, then somehow inserting a mocked instance is your next best choice. How to do the "dependency injection" depends on the exact context, such as the mocking/testing frameworks you are using.
Testing void methods in your main class under test is not a problem as does not require doAnswer.
Here is an example of how could you go about testing the call to unsubscribe.
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class TPresenterTest {
#InjectMocks
private TPresenter target = new TPresenter();
#Mock
private CompositeSubscription viewSubscription;
#Test
public void onDestroyShouldUnsubscribeWhenSubscriptionNotNullAndUnsubscribed() {
when(viewSubscription.isUnsubscribed()).thenReturn(false);
target.onDestroy();
verify(viewSubscription).unsubscribe();
}
#Test
public void onDestroyShouldNotUnsubscribeWhenSubscriptionNotNullAndNotUnsubscribed() {
when(viewSubscription.isUnsubscribed()).thenReturn(true);
target.onDestroy();
verify(viewSubscription, never()).unsubscribe();
}
}
As I mentioned in my comment to #GhostCat 's answer, my example is in fact un-testable because of the "new" instance of CompositeSubscription class. I would have to re-factor it and #GhostCat 's comment to his/her answer shows a way to do it.
#RunWith(MockitoJUnitRunner.Silent.class)
public class LoginActivityTest {
#InjectMocks
LoginActivity loginActivity;
private Pattern emailPattern;
#Before
public void createLogin(){
this.emailPattern = Patterns.EMAIL_ADDRESS;
}
#Test
public void checkValidation(){
mock(LoginActivity.class);
UserVO userVO = new UserVO();
userVO.setEmailID("invalid");
userVO.setPassword("a");
boolean b = loginActivity.validatesFields(userVO);
assertFalse(b);
}
}
this.emailPattern = Patterns.EMAIL_ADDRESS; This is creating null pointer object in MockitoJunitTestClass. But, when I run this on Activity it gets initialized properly.
Use PatternsCompat instead of Patterns
I was having a similar problem because it was just a simple test, but when I added #RunWith(AndroidJUnit4::class) the problem was fixed. Check if this is test that must run with Android resources or not.
I am a little confuse with your test:
You are mocking LoginActivity.class but not setting anything with that. I believe you want to do something like loginActivity = mock(LoginActivity.class); instead.
Also, your are mocking instead spying the class, so it won't access the real method in order to verify the flow of this method. In other words, your test is doing nothing in fact.
Finally, this emailPattern is never used on your test (probably it is used on you code), so I believe you want to mock it (I am supposing it). What I recommend you do is something like this:
#RunWith(MockitoJUnitRunner.Silent.class)
public class LoginActivityTest {
#Spy
#InjectMocks
private LoginActivity loginActivity;
#Mock
private OtherStuff otherStuff;
#Test
public void checkValidation(){
UserVO userVO = new UserVO();
userVO.setEmailID("invalid");
userVO.setPassword("a");
doReturn(Patterns.EMAIL_ADDRESS).when(otherStuff).doStuff();
boolean result = loginActivity.validatesFields(userVO);
assertFalse(result);
}
}
What I did here is just an example of unit test which is validating what validateFields() is doing. I suppose that inside this method you have some method on, what I name otherStuff, which calls a method that returns Patterns.EMAIL_ADDRESS, which is what you want to mock.
It would be really better if you insert the LoginActivity code to be more precise here, but I hope I helped you.
I have an application which displays data (posts) from a web API.
A background service syncs this data at some unknown time and saves it.
When visiting my main activity it loads this data and displays it in a RecyclerView
The loading is handled via a singleton class
I currently test the main activity as follows
#Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
#Test
public void testDataLoad() {
int postsTotal = DataSingleton.getInstance().getPostsCount();
ViewInteraction empty = onView(withId(R.id.empty_view));
ViewInteraction recycler = onView(withId(R.id.recycler_view));
if (postsTotal == 0) {
empty.check(matches(isDisplayed()));
recycler.check(matches(not(isDisplayed())));
} else {
empty.check(matches(not(isDisplayed())));
recycler.check(matches(isDisplayed()));
recycler.check(new RecyclerViewItemCountAssertion(greaterThan(postsTotal)));
}
}
I know that this can't be the right way to write tests. I want to be able to test both with an empty data set and a non-empty set so that the if-else is two separate tests. The only way I think I can achieve it is to mock the data.
Is there another way?
Can I use Mockito to make the MainActivity use mock data without modifying the production code? Is my only choice to make it inject either real or mocked data providers in place of my singleton?
Is it better to just uninstall and reinstall my app each time so there is no data to start with and then continue with real data testing?
Android Activity are heavyweight and hard to test. Because we don't have control over the constructor, it is hard to swap in test doubles.
The first thing to do is to make sure you are depending on an abstraction of the data-source rather than a concretion. So if you are using a singleton with a getPostsCount() method then extract an interface:
interface DataSourceAbstraction {
int getPostsCount();
}
Make a wrapper class that implements your interface:
class ConcreteDataSource implements DataSourceAbstraction {
#Override
int getPostsCount() {
return DataSingleton.getInstance().getPostsCount();
}
}
And make the Activity depend on that rather than the concrete DataSingleton
DataSourceAbstraction dataSourceAbstraction;
#Override
protected void onCreate(Bundle savedInstanceState) {
super(savedInstanceState);
injectMembers();
}
#VisibleForTesting
void injectMembers() {
dataSourceAbstraction = new ConcreteDataSource();
}
You can now swap in a test double by subclassing and overriding injectMembers that has relaxed visibility. It's a bad idea do this in enterprise development, but there are less options in Android Activities where you don't control the constructor of the class.
You can now write:
DataSourceAbstraction dataSource;
//system under test
MainActivity mainActivity
#Before
public void setUp() {
mockDataSource = Mockito.mock(DataSourceAbstraction.class);
mainActivity = new MainActivity() {
#Override
void injectMembers() {
dataSourceAbstraction = mockDataSource;
}
};
}
Within this method, I want to mock and ensure that mSharedPrefsManager gets called when I don't pass in a certain email string.
#Override
public void retrieveWithEmail(final String email, final WelcomeContract.Presenter presenter)
{
retrieveInteractor.buildRetrieveRequest(email, new RetrieveImpl.OnRetrieveCompletedListener()
{
#Override
public void onRetrieveCompleted(final MaitreBaseGson retrieveResponse, RetrieveImpl retrieveClass)
{
if (retrieveResponse.getStatus().equals(mContext.getString(R.string.ok)))
{
if (!email.equals("certain#email.com"))
mSharedPrefsManager.storePoints(Integer.parseInt(retrieveResponse.getData().getPoints()));
presenter.updateSilhouette(retrieveResponse);
}
// Silently swallow failures
}
});
}
However, with my test I'm not able to catch whether mSharedPrefsManager is called. Mockito says that .storePoints() is never called. I thought about doing a doReturn().when() but as this is within the method that wouldn't work, would it?
How do I catch the interactions on sharedPrefsManager?
Mockito also says that .updateSilhouette() is not called. Do I need to mock onRetrieveCompleted() somehow?
#RunWith(MockitoJUnitRunner.class)
public class WelcomeInteractorTest
{
#Mock
RetrieveImpl retrieveInteractor;
#Mock
WelcomePresenter welcomePresenter;
#Mock
SharedPrefsManager sharedPrefsManager;
#Mock
Context context;
#InjectMocks WelcomeInteractorImpl welcomeInteractor;
#Mock
RetrieveImpl.OnRetrieveCompletedListener onRetrieveCompletedListener;
#Test
public void RetrieveWithCertainEmail_SavePoints()
{
welcomeInteractor.retrieveWithEmail("certain#email.com", welcomePresenter);
verify(retrieveInteractor).buildRetrieveRequest(eq("certain#email.com"), any(RetrieveImpl.OnRetrieveCompletedListener.class));
verify(sharedPrefsManager).storePoints(any(Integer.class));
verify(welcomePresenter).updateSilhouette(any(MaitreBaseGson.class));
}
}
Attempting to use #Spy caused a lot of issues for me as RetrieveImpl interacts with a network.
I instead used a Captor and captured the callback.
#Captor
private ArgumentCaptor<RetrieveImpl.OnRetrieveCompletedListener> mOnRetrieveCompletedListenerCaptor;
...
#Test
public void isTest()
{
...
verify(retrieveInteractor).buildRetrieveRequest(eq(email), mOnRetrieveCompletedListenerCaptor.capture());
mOnRetrieveCompletedListenerCaptor.getValue().onRetrieveCompleted(mockMaitreBaseGsonSuccessful, retrieveInteractor);
}
You are mocking:
#Mock
RetrieveImpl retrieveInteractor;
This means that when you call retrieveInteractor.buildRetrieveRequest(..), the real implementation is not invoked and eventually the methods that you expect to be called within that method call are never called..
Try using #Spy instead, this will actually allow for the real implementation to be called and you can verify that object also:
#Spy
RetrieveImpl retrieveInteractor;
Just one the side.. in think you are testing too much there and going to deep in your verifications.
That test in my opinion should be done for the RetrieveImpl.OnRetrieveCompletedListener class. Not the one that is in your question.
But thats just to my taste..
I have MainActivity that shows FragmentDialog (EditIntervalFragment) in order to capture user's input. Activity implements EditIntervalListener interface. In onAtach method fragment casts activity to EditIntervalListener.
I want to test that my EditIntervalFragment properly calls EditIntervalListener methods with correct parameters.
My initial intent was to use Roblectric and Mockito. The following code almost works.
#Test
public void shouldCallInterfaceAfterModify() {
MainActivity hostActivity = Robolectric.setupActivity(MainActivity.class);
EditIntervalFragment editIntervalFragment = EditIntervalFragment.getInstance(0, TEST_NAME, TEST_DURATION);
editIntervalFragment.show(hostActivity.getSupportFragmentManager(), "test");
AlertDialog dialog = (AlertDialog) editIntervalFragment.getDialog();
assertNotNull(dialog);
EditIntervalFragment.EditIntervalListener activity = Mockito.spy(hostActivity);
dialog.findViewById(android.R.id.button1).performClick();
verify(activity).onIntervalChanged(0,TEST_NAME,TEST_DURATION);
}
The problem with this code that it uses real MainActivity. It means that all MainActivity's logic will be executed. I want to avoid this. How can I do this?
Update
I found a way to not call real MainActivity. I created another activity, just for test.
public class ActivityTest extends FragmentActivity implements EditIntervalFragment.EditIntervalListener {
//empty methods here
}
My test now looks like this
#Test
public void shouldCallInterfaceAfterModify() {
ActivityTest hostActivity = Robolectric.setupActivity(ActivityTest.class);
ActivityTest spy = Mockito.spy(hostActivity);
EditIntervalFragment editIntervalFragment = EditIntervalFragment.getInstance(0, TEST_NAME, TEST_DURATION);
editIntervalFragment.show(spy.getSupportFragmentManager(), "test");
AlertDialog dialog = (AlertDialog) editIntervalFragment.getDialog();
assertNotNull(dialog);
dialog.findViewById(android.R.id.button1).performClick();
verify(spy).onIntervalChanged(0, TEST_NAME, TEST_DURATION);
}
But after test execution I receive error saying than only spy.getSupportFragmentManager() was called. I'm 100% sure that onIntervalChanged should be called.
Looking for help. How can I implement such kind of test?
That is always challange to make work spies when you don't control lifecycle.
What we are usually doing we extracting all not related functionality to utility classes and mock them in tests. It also helps with design of the application (Single class responsibility rule).
Of course it depends if you do something with this data. If it is just data class than I would have Factory for creating this data classes and again mock it in tests. All this requires proper DI (look to Dagger).
And there is nothing wrong with your approach but it doesn't force you to think about your app as small parts that interact with each other. But at the same time it brings more complexity which pays off later
I ended up with this solution. Create an Activity that implements interface an keep track of all interaction.
public class ActivityTest extends FragmentActivity implements EditIntervalFragment.EditIntervalListener {
public int mIntervalChangedCalls = 0;
public int mPosition;
public String mName;
public long mDurationMillSec;
#Override
public void onIntervalChanged(int position, String name, long durationMillSec) {
mIntervalChangedCalls++;
mPosition = position;
mName = name;
mDurationMillSec = durationMillSec;
}
}
My test looks like this
#Test
public void shouldCallOnIntervalChanged() {
ActivityTest hostActivity = Robolectric.setupActivity(ActivityTest.class);
EditIntervalFragment editIntervalFragment = EditIntervalFragment.getInstance(0, TEST_NAME, TEST_DURATION);
editIntervalFragment.show(hostActivity.getSupportFragmentManager(), "test");
AlertDialog dialog = (AlertDialog) editIntervalFragment.getDialog();
assertNotNull(dialog);
dialog.findViewById(android.R.id.button1).performClick();
assertThat(hostActivity.mIntervalChangedCalls).isEqualTo(1);
assertThat(hostActivity.mPosition).isEqualTo(0);
assertThat(hostActivity.mName).isEqualTo(TEST_NAME);
assertThat(hostActivity.mDurationMillSec).isEqualTo(TEST_DURATION);
}
I'm not completely happy with this creation of a separate class just for test purposes. I suppose the same can be achieved with Mockito or Robolectric, but I do not know how.
So I'm still open for any ideas or suggestions. I'll accept my own answer, if no one gives better solution in a week.