Robolectric is-singlepane(/single-fragment)-test - android

I just started to try Robolectric and as I am currently playing around with the Dynamic Single/Dualpane-Fragment example from the google developer page I thought integrating it there and doing some basic tests.
The first thing I wanted to test is does the single/dualpane handling work correctly.
So its basically down to:
small device & portrait -> single-pane
large device & langscape -> dual-pane
As the code for the example is online (and as it is a standard template in Android-Studio) I am not gonna copy it here again. Just one thing: On launch the activity determines if its single- or dual pane by checking:
if (findViewById(R.id.exercise_detail_container) != null) [..]
It seems that for Robolectric its always dualpane.
So my singlepane test is pretty straight forward:
#Config(emulateSdk = 18)
#RunWith(RobolectricTestRunner.class)
public class SinglePaneTest {
private ExerciseListActivity activity;
private FragmentManager fragmentManager;
#Before
#Config(qualifiers = "port-small")
public void setup() {
this.activity = Robolectric.buildActivity(ExerciseListActivity.class).create().resume().get();
this.fragmentManager = activity.getFragmentManager();
}
#Test
#Config(qualifiers = "port-small")
public void testSinglePane() {
assertNull(activity.findViewById(R.id.exercise_detail_container));
}
}
But the test fails.
Can somebody tell me why? This should be working perfectly fine, shouldn't it?
Just for the record: Yep, in the emulator everything is working fine.

Try reversing order: small-port Don't know if will fix, but it is true that they must appear in the order in the table: http://developer.android.com/guide/topics/resources/providing-resources.html
if you use multiple qualifiers for a resource directory, you must add them to the directory name in the order they are listed in the table.
Robolectric example supports this ordering:
Qualifiers for the resource resolution, such as "fr-normal-port-hdpi".

This seems to be fixed with version 2.4 of robolectric!

Related

How to check the current Activity in a UI test

For tests I use Espresso and Barista
I have a test in which I need to open another screen by pressing a button. How can I check if this screen opens? Did the screen I need open?
Can I somehow check the chain of screens? To understand that the screens open in the order I need?
If someone throws links to good tutorials on UI tests in Android, I will be very grateful.
An easy solution would be to just check for an element of the new screen to be shown like this:
onView(withId(R.id.id_of_element_in_your_new_screen)).check(matches(isDisplayed()))
If you really want to check out for the current activity that is shown, you could try something like this:
Gather the current activity via InstrumentationRegistry and check for the activity in stage RESUMED.
fun getTopActivity(): Activity? {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
val resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED)
if (resumedActivities.iterator().hasNext()) {
resumedActivities.iterator().next()?.let {
activity = it
}
}
}
return activity
}
You could then check this in a test like this:
#Test
fun checkForActivity() {
val currentActivity = getTopActivity()
assertTrue(currentActivity?.javaClass == YourActivityToCheckAgainst::class.java)
}
I personally use intended(hasComponent(YourActivityToCheckAgainst::class.java.name)), which checks if the last intent was done with a desired activity, set as its component.
I also wrote an extensive Android UI testing tutorial using Espresso + Barista libraries.

How to rotate activity, I mean: screen orientation change using Espresso?

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
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!
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();
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.
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.
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.
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:
Do uiDevice.setOrientationLeft()
Application crashes;
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.
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

Generic mobile tests with appium

I'm new to mobile testing , and currently I research for an automation framework for mobile testing.
I've started to look into Appium, and created some tests for the demo app I've made (one for IOS and the other for Android).
I've managed to write a test for each of the platforms , but I was wondering , how difficult it might be to write a one generic test which will be executed on the both platforms with minimum adjustments ?
Thanks
It is possible but you have to keep same labels for each component for all platforms, for example to click on a button, instead of locating through Xpath locate by its name.
WebElement button = driver.findElement(By.name("my button")); button.click();
More info finding elements in Appium docs:
http://appium.wikia.com/wiki/Finding_Elements
I built an automation framework from scratch which does exactly the same thing, i.e. have one code base and the tests run both on Android and iOS based on what device and app you give the test. This is how I went about doing it. (I used Java+Appium+Cucumber framework).
Following the Page Object pattern is a good practice for writing automation code.
That being said, you will have all the resource ids of Android and Accessibility ids of iOS in 2 separate files under a folder named say "ObjectRepository". These files usually have the extension of *.properties (It is called the properties file).
Say you have a Login button that you want to interact with on Android and iOS, you have will 2 files:
File 1) "androidObject.properties" which has:
Login.LoginButton=loginAndroidBtn
File 2) "iOSObject.properties" which has:
Login.LoginButton=loginiOSBtn
NOTE: In the key/value pair above, the key is the same "Login.LoginButton", the value is the resource id and the accessibility id of the Login Button in your Android and iOS application
In your code you would do the following:
if(IS_ANDROID) {
DRIVER.findElementById("Login.LoginButton").click();
} else {
DRIVER.findElementByAccessibilityId("Login.LoginButton").click();
}
In another file you would set what IS_ANDROID and IS_IOS means. You may do something like this:
public static DeviceConfig DEVICE_CONFIG;
private void setPlatform() {
if (DEVICE_CONFIG.platformName.equals("Android")) {
IS_ANDROID = true;
} else if (DEVICE_CONFIG.platformName.equals("iOS")) {
IS_IOS = true;
}
This way you can have one code base and run Android and iOS seamlessly.

how to use fest for android

I am trying to use FEST with an android app testing
I want to check if some views in the activity got created and initialized correctly
#Test
public void testSplashScreenActivity() throws Exception{
SplashScreenActivity activity = Robolectric.buildActivity(SplashScreenActivity.class).create().start().resume().get();
//here i want to write fest assertions
}
however, when i assertThat(activity.welcomeText)..... nothing is allowed after this line?
i wrote import static org.fest.assertions.api.ANDROID.assertThat;
in my imports, yet the api doesnt seem to be available
what to do to make my test project give me the api i need ?

How to import a SpriteFont into MonoGame

I'm porting a simple tetris-like XNA app to Android, using Mono For Android and MonoGame; I have followed the suggested steps in this link and so far, everything compiles well, and no relevant warnings fire up. However, upon loading the contents, a null parameter exception breaks the program at the point below in my program:
protected override void LoadContent() {
// ...
_font = Content.Load<Microsoft.Xna.Framework.Graphics.SpriteFont>("SpriteFont1");
// ...
}
The content root directory is set in the game constructor class:
public Game2 (){
Content.RootDirectory = "Content";
Content.RootDirectory = "Assets/Content"; // TEST.
//...}
And I have tried several combinations, all to no avail.
I have also tried setting the xnb files as Content as well as Android Assets in the Build Action property; having the linked, copied always, copied only if newer... etc.
Either way, my problem is that I don't really understand WHY and HOW should I do this. I'm rather new to the platform and to XNA as well, so this may very well be a newbie question, but the truth is after several hours banging my head and fists against the monitor/keyboard I feel stuck and need your help.
I have a library that supports variable-width fonts (generated by BMFont) on MonoGame. Unfortunately it is a renderer and so has other code around it. However, the basic idea is very simple. You can take a look at the loader here and the mesh builder (given a string) here. This builder supports fonts that spread characters across multiple pages, too.
Hope this helps!
MonoGame (2.5.1) throws NotImplementedException in ContentManager.Load for SpriteFont type. Have the same not resolved problem. I'm trying not to use DrawString.
For loading textures in Win32 application I use:
Content.RootDirectory = #"../../Content";
var sampleTexture = Content.Load<Texture2D>("Sample.png");
You even must not add it to solution.
For Andoind (MonoDroid) application you must add "Content" folder to your solution and set "Andtoid Asset" in "Sample.png" properties.
Content.RootDirectory = "Content";
var sampleTexture = Content.Load<Texture2D>("Sample.png");
See also:
http://monogame.codeplex.com/discussions/360468
http://monogame.codeplex.com/discussions/267900

Categories

Resources