Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an explanation of why using mocking frameworks to mock Android classes like Activity/Context/etc. is a bad idea #9007

Open
bacecek opened this issue Apr 19, 2024 · 2 comments

Comments

@bacecek
Copy link
Contributor

bacecek commented Apr 19, 2024

I found the following recommendation in various issues about using mocking frameworks for mocking Android classes like Activity/Context/etc.:

You should never mock Context objects using mocking frameworks. You should only be using Robolectric APIs to customize the behavior of Android classes. At Google mocking objects like Context, Activity, etc.. are banned.

Unfortunatelly, I've not found this recommendation in Android open source projects:
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:docs/testing.md
https://cs.android.com/android/platform/superproject/main
or this robolectric repo.

Moreover, when any Android related class is used in test without Robolectric runner, the following exception fails the test:

Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.RuntimeException: Method isEmpty in android.text.TextUtils not mocked. See https://developer.android.com/r/studio-ui/build/not-mocked for details. [in thread "Test worker"]
	at android.text.TextUtils.isEmpty(TextUtils.java)
	...

which points to the page https://developer.android.com/training/testing/local-tests#mocking-dependencies, where example shows that mocking Context is okay.

Could someone explain why mocking Android classes is the a bad idea and why?

@utzcoz
Copy link
Member

utzcoz commented Apr 19, 2024

@bacecek It's not recommended using mock with Robolectric together. You can only use mock for one test file, for example mocking Activity, Context by yourself. You can only use Robolectric for your one test file by leveraging Robolectric's fake implementations of Android Frameworks. But it's not allowed/recommended to use them together as they use similar mechanism to modify class not and generate extra code for mocking purpose or shadow purpose, and sometimes they have conflicted with each other.

@hoisie
Copy link
Contributor

hoisie commented Apr 23, 2024

cc @charlesmunger

It is generally OK to mock Android objects, but there are a certain set of them that I would not recommend mocking. They are banned from being mocked internally at Google. These are the very large, complex classes like Context, Fragment, Resources, View, Activity, Application, etc..

Let's take Context for example. It's a very very complex class with hundreds of methods, many of which are @hide API, so these hidden methods are only invoked by other classes in the Android framework.

There are a lot of Android classes that take Context as a parameter. One I dealt with today was ViewConfiguration.get(Context), which builds a ViewConfiguration given the Context.

Let's say you are using a mock Context when calling ViewConfiguration.get(Context). You would have to implement:

  • Context.getResources()
  • A ton of methods on the Resources object returned by Context.getResources()
  • Context.getDisplayId() - this is a hidden @hide method

Now let's say that the new Version of Android introduced some new properties in ViewConfiguration, and some of those are derived from other methods in Context. You will now have to update the Context mock to try to return sensible values for those.

So you will typically feel the pain when you update the SDK level used in Robolectric tests.

Perhaps for a small development team this would not be a large burden. But at scale, for a company like Google, multiply these issues by hundreds of apps and thousands of developers, and updating the Android SDK becomes a very large challenge.

If people standardize on using Context objects built in Robolectric, there is a single place that needs to be changed if there are incompatibilities.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants