0

I'm a new one in android dev, so I have an app which contain viewPager with 2 UI fragments and 1 nonUIFragment in which operations are performed (i used "setRetainInstance(true)", it deprecated, but i must use it). In this nonUIFragment i have Handler which accepts messages from operations started with ExecutorServices. But now my task is test this app with Mockito and i'm totaly confused.

Mentor said "you have to mock the operation that produces the result, is performed in a nonUIFragment, and its result is stored in a collection."

How must look this test, I can't create spy() class NonUIFragment and use real methods because of "Method getMainLooper in android.os.Looper not mocked." All of my methods are void, they don't returne something, how can i trace this chain.

NonUIFragment.java

private NonUIToActivityInterface nonUIInterface;
    private final Map<DefOperandTags, HashMap<DefOperationTags, String>> allResultsMap
        = new HashMap<>();

 @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

//Handler pass result to here
    public void passAndSaveResult(DefOperandTags operandTag, DefOperationTags operationTag, String result) {
        allResultsMap.get(operandTag)).put(operationTag, result);
    }

private final Handler handler = new Handler(Looper.getMainLooper()) {
        public void handleMessage(Message msg) {
            if (msg.what != null)
                passAndSaveResult(defOperandTags, defOperationTag, msg.obj.toString());
    };

OneOfOperation.java (add value to the List)

public class AddToStartList extends Operation {

    public AddToStartList(List list, DefOperationTags operationTag) {
        super(list);
        key = operationTag;
    }

    @Override
    public void operation(Object collection) {
        ((List)collection).add(0, "123");
    }

So, how can I implement what my mentor said?

Sergei K.
  • 1
  • 2
  • Mockito offers several ways to mock things, you will need to find the ones that will work in your situation. For example Mockito has a class of mocking methods for mocking methods that have no return value, i.e are void. I think you need to be looking for how to use Mockito for Android development. – Gavin Feb 04 '22 at 07:37

1 Answers1

0

This is going to be tricky, because your Android testing library has no implementations, and static methods are generally more difficult to mock safely and effectively.

Recent versions of Mockito have added the ability to mock static methods without using another library like PowerMock, so the first choice would be something like that. If at all possible, use mockStatic on Looper::getMainLooper to mock.

Another solution is to add some indirection, giving you a testing seam:

public class NonUIFragment extends Fragment {
  /** Visible for testing. */
  static Looper overrideLooper;

  // ...

  private final Handler handler = new Handler(
      overrideLooper != null ? overrideLooper : Looper.getMainLooper()) {
    /* ... */
  };
}

Finally, if you find yourself doing this kind of mock a lot, you can consider a library like Robolectric. Using Robolectric you could simulate the looper with a ShadowLooper, which would let you remote-control it, while using Mockito for any classes your team has written. This would prevent you from having to mock a realistic Looper for every test, for instance.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251