29

I have decided that one of the testing criteria for my application tests with Google's Espresso is:

Test should maintain Activity state after screen orientation rotation

How do I rotate the screen when using Espresso?


I have tried the following Robotium code (Yes I placed Robotium code in my Espresso test so sue me)

solo.setActivityOrientation(solo.LANDSCAPE);
solo.setActivityOrientation(solo.PORTRAIT);

but It crashes the application when I run it within my Espresso test.
Is there any way to do this?

Thanks in advance for any help

piotrek1543
  • 19,130
  • 7
  • 81
  • 94
FalconBot
  • 419
  • 1
  • 4
  • 12
  • Look at the accepted answer here: http://stackoverflow.com/questions/3515959/how-do-you-force-an-orientation-change-in-an-android-instrumentation-test – Clive Seebregts May 21 '16 at 12:52

7 Answers7

21

If you have the only Activity in your test case, you can do:

1. Declare you test Rule.

@Rule
public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(TestActivity.class);

2. Get you Activity and apply a screen rotation.

mActivityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mActivityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

That's a piece of pie!

Slava
  • 1,314
  • 14
  • 28
  • 2
    This is only part of the solution. It seems that you need to wait a bit after. – 0xcaff Apr 07 '17 at 23:17
  • I'm sorry, but it seems it works: https://github.com/egslava/edittext-mask/blob/master/MaskedEditText/src/androidTest/java/ru/egslava/lib_phone/MainActivityTest.java#L91 – Slava Apr 08 '17 at 15:32
16

You can do it with uiautomator library

dependencies {
  androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' 
}

ui automator require min sdk version 18 so if your app has a lower min sdk you need to create a new AndroidManifest.xml in androidTest folder

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:tools="http://schemas.android.com/tools"
    package="your.package.name">
    <uses-sdk tools:overrideLibrary="android.support.test.uiautomator.v18"/>
</manifest>

and then in your test

UiDevice device = UiDevice.getInstance(getInstrumentation());

device.setOrientationLeft();
device.setOrientationNatural();
device.setOrientationRight();
lelloman
  • 13,883
  • 5
  • 63
  • 85
  • This is the best way and clean one to handle orientation while testing. – TeeTracker Sep 03 '18 at 11:15
  • this does not work if the device has auto-rotation disabled. Is there a way around this? – Kirk Nov 22 '18 at 20:47
  • @Kirk are you sure? If I recall correctly, it should work – lelloman Nov 22 '18 at 21:18
  • @Kirk I just tested this on my phone running Android 9 and it rotates even with disabled auto-rotation – lelloman Nov 23 '18 at 13:39
  • With AndroidX, the Gradle dependency is now [`androidx.test.uiautomator:uiautomator:2.2.0`](https://mvnrepository.com/artifact/androidx.test.uiautomator/uiautomator) – anotherdave Apr 28 '20 at 13:56
  • I am a stickler to test in Jellybean (SDK 16) as a minimum, unless the whole project hinges on it, so I tried and was successful with the following without additional libraries: `InstrumentationRegistry.getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);` `InstrumentationRegistry.getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_270);` – Philip Young Jul 22 '21 at 14:26
7

This more complete solution creates a custom Espresso ViewActionand works well. It shows how to get the Activity (even when it is an AppCompatActivity) before calling its setRequestedOrientation() method. It also has a clean caller interface:

onView(isRoot()).perform(orientationLandscape()); 
onView(isRoot()).perform(orientationPortrait());

I follow each orientation change with a 100 ms delay, though you may not need it.

barnabas
  • 129
  • 2
  • 4
5

How to rotate the screen:

public static void rotateScreen(Activity activity) {
    final CountDownLatch countDownLatch = new CountDownLatch(1);
    final int orientation = InstrumentationRegistry.getTargetContext()
            .getResources()
            .getConfiguration()
            .orientation;
    final int newOrientation = (orientation == Configuration.ORIENTATION_PORTRAIT) ?
            ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE :
            ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;

    activity.setRequestedOrientation(newOrientation);

    getInstrumentation().waitForIdle(new Runnable() {
        @Override
        public void run() {
            countDownLatch.countDown();
        }
    });

    try {
        countDownLatch.await();
    } catch (InterruptedException e) {
        throw new RuntimeException("Screen rotation failed", e);
    }
}

The Activity can be obtained from the ActivityRule.

aha
  • 3,702
  • 3
  • 38
  • 47
1

You can't mix Robotium and Espresso tests. The best way sometimes to solve any issue is to check source code of desired but not comaptible method.

I'm pretty sure that you have already setUp() method, which has code like:

myActivity = this.getActivity();

Use this to change your screen orientation change:

myActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

or

myActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

You may also need to use myActivity.getInstrumentation().waitForIdleSync(); or Thread.sleep(milliseconds); in order to wait for the rotation end because it is performed in Async manner. The second methods depends on emulator/device so choose it wisely.

Hope it help.

piotrek1543
  • 19,130
  • 7
  • 81
  • 94
1

In addition, if you are using ActivityTestRule in order to access your activity, the documentation states that this class is deprecated and you are better off using ActivityScenarioRule.

Instead of directly accessing properties of the activity, you need to put your interactions with the activity inside a runnable callback.

So the screen rotation example may look something like this:

    @get:Rule
    val activityRule = ActivityScenarioRule(MainActivity::class.java)

    private fun rotate() {
        activityRule.scenario.onActivity {
            it.requestedOrientation =
                    ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
        }
    }

P.S. not sure about the proper constant to use, but ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE(or USER_PORTRAIT) was the one that worked for me

0

After my own troubles with testing of the orientation I want to add that while lelloman's advice to use UiDevice is correct from the documentation standpoint - unfortunately it doesn't work as expected in some cases.

I found that at least on the API 23 emulator rotation could stuck after the test's crash:

  1. Do uiDevice.setOrientationLeft()
  2. Application crashes;
  3. Rotation is stuck;
    3.1 Forcing rotation through UiDevice works but when test ends rotation is back the wrong one until you manually change rotation to "Auto-rotate";
    3.2 uiDevice.unfreezeRotation() helps while test is running but after the test's end rotation is back to the wrong one.

I don't have this issue on API 28.

So I found that using setRequestedOrientation is the only solution for me.

Ricardo Gonzalez
  • 1,827
  • 1
  • 14
  • 25
werman
  • 21
  • 1
  • 1
  • 2