I'm new to the automated testing, and using espresso to test my android App.
The problem is that I have multiple dynamic views depending on certain conditions :
My user has a boolean attribute, let's call it "isPremium"
when I click on a button my user is redirected to FragmentA if isPremuim == true , else he's redirected to FragmentB.
now for my tests I have
#Test public void testFragmentA();
and
#Test public void testFragmentB();
but when I run my tests based on my data, forcibly one of the two tests fails.
so should i make one test for both fragments like
private void testFragmentA();
private void testFragmentB();
#Test
public void myGlobalTest {
if(user.isPremium) testFragmentA();
else testFragmentB();
}
is this the right way to make my tests ? or there is another better way, because sincerly I'm not convinced with this method.
It would be best if you set value for premium at the beginning of each test (true for testFragmentA, false for testFragmentB). That way you will know what you are expecting and what each fragment depends on.
Also, if user is some global variable, you should keep its state in #Before and restore it in #After method.
boolean isPremium;
#Before
public void init() {
isPremium = User.isPremium();
}
#Test
public void testFragmentA(){
User.setPremium(true);
// test fragment A
}
#Test
public void testFragmentB(){
User.setPremium(false);
// test fragment B
}
#After
public void restore() {
User.setPremium(isPremium);
}
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.
I have experienced next scenario:
each test in my test-project uses separate activity
test1 (main screen) belong to activity#1, there is a button on this screen which can change state from STATE#1 (OFF) to STATE#2 (ON)
tap on button STATE#1 (from test1) cause raise of another screen which belong to activity#2 (there is test2 starting), then on act.#2 user perform some actions which should change button state to STATE#2
but in my test button STATE#2 wouldn't be refreshed and passed to the previous the activity#1
Do I need to sync test data in specific way? If button's state (which is on activity#1) can be changed from another test activity (activity#2)
Here is example of what I'm doing:
First of all test after which button state should be changed
TEST2 - test that should change button1 state
#RunWith(AndroidJUnit4.class)
public class ChangeBtnState extends MyIdle
{
#Rule
public ActivityTestRule<Activity#2> EspressoTestRule#2 = new ActivityTestRule<>(Activity#2.class, false, true);
private MyIdle IdlingRecourseActivity#2;
#Before
public void SetUpTest2()
{
EspressoTestRule#2.getActivity().getSupportFragmentManager().beginTransaction();
IdlingRecourseActivity#2 = (ESP_idling) EspressoTestRule#2.getActivity().getIdlingResource();
Espresso.registerIdlingResources(IdlingRecourseActivity#2);
}
#Test
public void StartTestRule2()
{
// Just some actions on activity#2 after which button1 state should be from STATE#1 to STATE#2
ViewInteraction ButtonSendtoOFF = Espresso.onView(allOf(ViewMatchers.withId(android.R.id.SomebuttonONact#2))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
ButtonSendtoOFF.preform(click()); // here click
}
}
#After
public void unregistereSetUpTest2()
{
if (IdlingRecourseActivity#2 != null)
{
Espresso.unregisterIdlingResources(IdlingRecourseActivity#2);
}
}
}
Now checking does button state changed on activity#1
#RunWith(AndroidJUnit4.class)
public class CheckBtnState extends MyIdle
{
#Rule
public ActivityTestRule<Activity#1> EspressoTestRule#1 = new ActivityTestRule<>(Activity#1.class, false, true);
private MyIdle IdlingRecourseActivity#1;
#Before
public void SetUpTest1()
{
EspressoTestRule#1.getActivity().getSupportFragmentManager().beginTransaction();
IdlingRecourseActivity#1 = (ESP_idling) EspressoTestRule#1.getActivity().getIdlingResource();
Espresso.registerIdlingResources(IdlingRecourseActivity#1);
}
#Test
public void StartTestRule1()
{
// Check some actions FROM activity#2 to change **button1** from STATE#1 to STATE#2
ViewInteraction ButtonSTATE = Espresso.onView(allOf(ViewMatchers.withId(android.R.id.Button1), withText("OFF"))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
//ButtonSTATE.preform(click()); // here click
}
#After
public void unregistereSetUpTest1()
{
if (IdlingRecourseActivity#1 != null)
{
Espresso.unregisterIdlingResources(IdlingRecourseActivity#1);
}
}
}
BTW, I have look through a lot of topics and even found examples with intents, but it doesn't work for me, perhaps there is should be specific structure.
If you need extra info please add your question in comments.
The problem is that you are writing two tests such that the second test relies on the state of the first one. This is incorrect test design. Every test should be able to run completely independent of any other tests. You need to rethink what each of your tests are testing. What happens in the second activity that affects the state of the first one? Are you using startActivityForResult()? If so, your test for the second activity should verify that the expected result is set from the second activity. This test should not rely on anything in the first activity.
I'm using Espresso to build UI tests. For some test cases I would like to call a specific after step to reset the state in case the script fails.
Is there a way to perform an #After step for a single JUnit test case (#Test)?
The only solution I can think of is to make a separate test class. But I would like the test cases to be grouped in the same test class.
It does sound a little odd ;) but ...
You could add a try/finally to the single test for which you want this after behaviour. For example:
#Test
public void testA() {
try {
// the body of testA
} finally {
// apply the 'after' behaviour for testA
}
}
Or, if you really want to use JUnit's #After then you could use the TestName Rule (since JUnit 4.7) as follows:
#Rule
public TestName testName = new TestName();
#After
public void conditionalAfter() {
if ("testB".equals(testName.getMethodName())) {
System.out.println("apply the 'after' behaviour for testB");
}
}
#Test
public void testA() {
}
#Test
public void testB() {
}
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;
}
};
}
I am trying to find a way to verify that an activity has finished loading everything only without doing any changes in the application code. The method mentioned in this question and many others requires some application code change and I would like to do it via androidTest section.
There are scenarios when the activity is not fully loaded and running the following code fails:
onView(allOf(withId(R.id.user_name), withText("username1"))).perform(click());
In this example I am waiting for a ListView to load, so the data may also be loaded asynchronously (I am using espresso).
Might be too late but you should take a look at espresso idling resource to sync your background loading tasks with espresso. You wont need to change any of your application code. Here you have a deeper insight on android custom idling resources: http://dev.jimdo.com/2014/05/09/wait-for-it-a-deep-dive-into-espresso-s-idling-resources/
or this http://blog.sqisland.com/2015/04/espresso-custom-idling-resource.html
Here is what I did to make espresso wait for my list to be populated (from data comming from the network) before running the UI test.
public class ListResultsIdlingResource implements IdlingResource {
private ResourceCallback callback;
private RecyclerView mRecyclerListings;
public ListResultsIdlingResource(RecyclerView recyclerListings) {
mRecyclerListings = recyclerListings;
}
#Override
public boolean isIdleNow() {
if (mRecyclerListings != null && mRecyclerListings.getAdapter().getItemCount() > 0) {
if (callback != null) {
callback.onTransitionToIdle();
}
return true;
}
return false;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.callback = callback;
}
#Override
public String getName() {
return "Recycler idling resource";
}
You just have to check that your list has items in you isIdleNow() method before running your UI test over its elements.
And in your espresso test class in your setup method register your idling resource and pass it a reference to your ListView or Recyclerview or any view you are using as list.
#Before
public void setUp() throws Exception {
Intent intent = createIntentForActivity();
activityRule.launchActivity(intent);
mActivity = activityRule.getActivity();
mListResultsIdlingResource = new ListingResultsIdlingResource(
(RecyclerView) mActivity.findViewById(R.id.recycler_view));
registerIdlingResources(mListResultsIdlingResource);
}
Hope this is helpful for anyone looking for this.