I recorded an Espresso test using the recorder in Android Studio 4.2.2, which included a single assertion that a text field on my MainActivity UI was showing with the correct text string. I then saved this to SplashActivityTest.java:
public class SplashActivityTest {
#Rule
public ActivityTestRule<SplashActivity> mActivityTestRule = new ActivityTestRule<>(SplashActivity.class);
#Before
public void registerIdlingResource() {
IdlingRegistry.getInstance().register(CountingIdlingResourceSingleton.espressoIdlingResource);
}
#After
public void unregisterIdlingResource() {
IdlingRegistry.getInstance().unregister(CountingIdlingResourceSingleton.espressoIdlingResource);
}
#Test
public void splashActivityTest() {
ViewInteraction textView = onView(
allOf(withId(R.id.playlistText), withText("My Playlists"),
withParent(withParent(withId(R.id.nav_host_fragment))),
isDisplayed()));
textView.check(matches(isDisplayed()));
ViewInteraction textView2 = onView(
allOf(withId(R.id.playlistText), withText("My Playlists"),
withParent(withParent(withId(R.id.nav_host_fragment))),
isDisplayed()));
textView2.check(matches(withText("My Playlists")));
}
}
I added use of the Idling registry to this class because in my app what actually happens is a Splash screen is the launcher activity, which then launches the activity that loads the UI that I want to test.
I have this code:
// Necessary for automated tests, decrement handled in MainActivity.onResume()
CountingIdlingResourceSingleton.increment();
In the onCreate() method of SplashActivity and this code:
// Necessary for automated tests - increment is done in SplashActivity.onCreate()
CountingIdlingResourceSingleton.decrement();
At the end of onResume() in MainActivity.
The above code runs flawlessly, the test succeeds. Yay.
However, I get a deprecation warning on my use of ActivityTestRule, in favor of using an ActivityScenarioRule instead of ActivityTestRule (kind of interesting since use of that API was what was generated by the Espresso recorder in the latest 4.2.2 Android Studio, but that's the subject of a different post!).
So I change it:
public class SplashActivityTest {
#Rule
public ActivityScenarioRule<SplashActivity> mActivityTestRule = new ActivityScenarioRule<>(SplashActivity.class);
#Before
public void registerIdlingResource() {
IdlingRegistry.getInstance().register(CountingIdlingResourceSingleton.espressoIdlingResource);
}
#After
public void unregisterIdlingResource() {
IdlingRegistry.getInstance().unregister(CountingIdlingResourceSingleton.espressoIdlingResource);
}
#Test
public void splashActivityTest() {
ViewInteraction textView = onView(
allOf(withId(R.id.playlistText), withText("My Playlists"),
withParent(withParent(withId(R.id.nav_host_fragment))),
isDisplayed()));
textView.check(matches(isDisplayed()));
ViewInteraction textView2 = onView(
allOf(withId(R.id.playlistText), withText("My Playlists"),
withParent(withParent(withId(R.id.nav_host_fragment))),
isDisplayed()));
textView2.check(matches(withText("My Playlists")));
}
}
Now it no longer runs flawlessly. My app starts, the application class runs, but the launcher class never is called. Instead I get:
java.lang.AssertionError: Activity never becomes requested state "[DESTROYED, CREATED, STARTED, RESUMED]" (last lifecycle transition = "PRE_ON_CREATE")
Why? What do I need to do differently to insure that my normal launcher activity is called?
Related
I'm using Java+TestNG+Allure. I need to get all test fails in Allure report, not only the first fail of the test but all, and the test should run from the beginning to the end despite failed steps.
For reporting the test failures in Allure report we have to do little bit of modifications in Allure Class. Here we want to report any of the sub step as a failure, execute the remaining steps and then mark the main test step as a failed test. For doing this we can use the concept of SoftAssertions. I had created one class called as AllureLogger. Inside the class we will have 5 Methods.
1)starttest() 2)endtest() 3) markStepAsPassed(String message) 4)marstepAsFailed(String message) 5)logStep().
public class AllureLogger {
public static Logger log = Logger.getLogger("devpinoylog");
private static StepResult result_fail;
private static StepResult result_pass;
private static String uuid;
private static SoftAssert softAssertion;
public static void startTest() {
softAssertion = new SoftAssert();
}
public static void logStep(String discription) {
log.info(discription);
uuid = UUID.randomUUID().toString();
result_fail = new StepResult().withName(discription).withStatus(Status.FAILED);
result_pass = new StepResult().withName(discription).withStatus(Status.PASSED);
}
public static void markStepAsFailed(WebDriver driver, String errorMessage) {
log.fatal(errorMessage);
Allure.getLifecycle().startStep(uuid, result_fail);
Allure.getLifecycle().addAttachment(errorMessage, "image", "JPEG", ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES));
Allure.getLifecycle().stopStep(uuid);
softAssertion.fail(errorMessage);
}
public static void markStepAsPassed(WebDriver driver, String message) {
log.info(message);
Allure.getLifecycle().startStep(uuid, result_pass);
Allure.getLifecycle().stopStep(uuid);
}
public static void endTest() {
softAssertion.assertAll();
softAssertion = null;
startTest();
softAssertion = new SoftAssert();
}
}
In the above class, we are using different methods from allureClass and we are doing little bit of modification to add soft assertions.
Everytime we start a TestMethod in testClass we can call the starttest() and end testmethod().Inside the test methods if we have some substeps we can use try catch block to mark the substeps as pass or fail.Ex-Please check the below test method as an Example
#Test(description = "Login to application and navigate to Applications tab ")
public void testLogin()
{
AllureLogger.startTest();
userLogin();
navigatetoapplicationsTab();
AllureLogger.endTest();
}
Above is a test method which will login to one application and then navigate to application tab.Inside we have two methods which will be reported as substeps, 1)login()- For logging in the application 2) navigatetoapplicationsTab()-to navigate to application tab. If any of the substep fails then the main step and substep will be marked as fail and remaining steps will be executed.
We will define the body of the above functions which are defined in test method as below:
userLogin()
{
AllureLogger.logStep("Login to the application");
try
{
/*
Write the logic here
*/
AllureLogger.MarStepAsPassed(driver,"Login successful");
}
catch(Exception e)
{
AllureLogger.MarStepAsFailed(driver,"Login not successful");
}
}
navigatetoapplicationsTab()
{
AllureLogger.logStep("Navigate to application Tab");
try
{
/*
Write the logic here
*/
AllureLogger.MarStepAsPassed(driver,"Navigate to application Tab successful");
}
catch(Exception e)
{
e.printStackTrace();
AllureLogger.MarStepAsFailed(driver,"Navigate to application Tab failed");
}
}
Everytime any exception is thrown they will be caught in catch block and reported in the Allure Report. The soft assertion enables us to execute all the remaining steps successfully.
Attached is a screenshot of an Allure report generated by using the above technique.The main step is marked as Failed and remaining test cases have got executed.
The report attached here is not from the above example which is mentioned. It is just a sample as how the report would look.
I have the following AppDelegate which takes quite some time to load:
Syncfusion.ListView.XForms.iOS.SfListViewRenderer.Init();
new Syncfusion.SfNumericUpDown.XForms.iOS.SfNumericUpDownRenderer();
Syncfusion.SfCarousel.XForms.iOS.SfCarouselRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfSegmentedControlRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfCheckBoxRenderer.Init();
new Syncfusion.XForms.iOS.ComboBox.SfComboBoxRenderer();
//Syncfusion.XForms.iOS.TabView.SfTabViewRenderer.Init();
new Syncfusion.SfRotator.XForms.iOS.SfRotatorRenderer();
new Syncfusion.SfRating.XForms.iOS.SfRatingRenderer();
new Syncfusion.SfBusyIndicator.XForms.iOS.SfBusyIndicatorRenderer();
What options should I consider when I know some of these components aren't needed for the main screen, but for subscreens?
I am using PRISM, and it appears that every tab is pre-loaded immediately before allowing display or interaction with the end user. What can I do to delay the pre-rendering that the Prism TabView does prior to showing the interface?
Should I use Lazy<T>? What is the right approach?
Should I move these components to another initialization section?
There are a number of ways you could ultimately achieve this, and it all depends on what your real goals are.
If your goal is to ensure that you get to a Xamarin.Forms Page as fast as possible so that you have some sort of activity indicator, that in essence says to the user, "it's ok I haven't frozen, we're just doing some stuff to get ready for you", then you might try creating a "SpashScreen" page where you do additional loading. The setup might look something like the following:
public partial class AppDelegate : FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App(new iOSInitializer()));
return base.FinishedLaunching(app, options);
}
}
}
public class iOSInitializer : IPlatformInitializer, IPlatformFinalizer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance<IPlatformFinalizer>(this);
}
public void Finalize()
{
new Syncfusion.SfNumericUpDown.XForms.iOS.SfNumericUpDownRenderer();
Syncfusion.SfCarousel.XForms.iOS.SfCarouselRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfSegmentedControlRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfCheckBoxRenderer.Init();
}
}
public class App : PrismApplication
{
protected override async void OnInitialized()
{
await NavigationService.NavigateAsync("SplashScreen");
}
}
public class SplashScreenViewModel : INavigationAware
{
private IPlatformFinalizer _platformFinalizer { get; }
private INavigationService _navigationService { get; }
public SplashScreenViewModel(INavigationService navigationService, IPlatformFinalizer platformFinalizer)
{
_navigationService = navigationService;
_platformFinalizer = platformFinalizer;
}
public async void OnNavigatedTo(INavigationParameters navigationParameters)
{
_platformFinalizer.Finalize();
await _navigationService.NavigateAsync("/MainPage");
}
}
If you're working with Modules you could take a similar approach though any Modules that would initialize at Startup would still be making that call to Init the renderers before you've set a Page to navigate to. That said, working with Modules does give you a number of benefits here as you only ever would have to initialize things that the app actually requires at that point.
All of that said I'd be surprised if you see much in the way of gain as these Init calls are typically empty methods only designed to prevent the Linker from linking them out... if you aren't linking or have a linker file you could simply instruct the Linker to leave your Syncfusion and other libraries alone.
The new obsolete warning in Xamarin.Forms 2.5 really puzzled me.
What context should I be using in Dependency Services, for example, to call GetSystemService()?
Should I store in a static field the context of activity the xamarin forms were initialized against?
Should I override the android Application class and use its Context?
Should I call GetSystemService at activity create and save it somewhere?
I was having the same issue with several Dependency Services
The simplest solution
In a lot of cases for Single Activity Applications
Xamarin.Forms.Forms.Context
Can be replaced with
Android.App.Application.Context
The Background in more detail
Android.App.Application.Context returns the global Application Context of the current process tied to the lifecycle of the Application, as apposed to an Activity context.
A typical example of using the Application context is for starting an Activity e.g.
Android.App.Application.Context.StartActivity(myIntent);
The general rule of thumb is to use the current Activity Context, unless you need
to save a reference to a context from an object that lives beyond your
Activity. In which case use the Application context
Why did Forms.Context go obsolete?
Xmarin.Forms 2.5 introduced a new "Forms embedding" feature, which can embed Forms pages into Xamarin.iOS / Xamarin.Android apps. However, since Xamarin.Android apps can use multiple Activities, seemingly there was a danger of Xamarin.Android users calling Forms.Context and in turn getting a reference to the MainActivity, which has the potential cause problems.
The work around
Inside a Renderer you now get a reference to the view’s context which is passed into the constructor.
With any other class you are faced with the issue of how to get the Activity Context. In a single Activity application (in most cases) the Application.Context will work just fine.
However to get the current Activity Context in a Multiple Activity Application you will need to hold a reference to it. The easiest and most reliable way to do this is via a class that implements the Application.IActivityLifecycleCallbacks Interface.
The main idea is to keep a reference of the Context when an Activity
is created, started, or resumed.
[Application]
public partial class MainApplication : Application, Application.IActivityLifecycleCallbacks
{
internal static Context ActivityContext { get; private set; }
public MainApplication(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer) { }
public override void OnCreate()
{
base.OnCreate();
RegisterActivityLifecycleCallbacks(this);
}
public override void OnTerminate()
{
base.OnTerminate();
UnregisterActivityLifecycleCallbacks(this);
}
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
{
ActivityContext = activity;
}
public void OnActivityResumed(Activity activity)
{
ActivityContext = activity;
}
public void OnActivityStarted(Activity activity)
{
ActivityContext = activity;
}
public void OnActivityDestroyed(Activity activity) { }
public void OnActivityPaused(Activity activity) { }
public void OnActivitySaveInstanceState(Activity activity, Bundle outState) { }
public void OnActivityStopped(Activity activity) { }
}
With the above approach, single Activity Applications and multiple Activity Applications can now always have access to the Current/Local Activity Context. e.g instead of relying on the global context
Android.App.Application.Context
// or previously
Xamarin.Forms.Forms.Context
Can now be replaced with
MainApplication.ActivityContext
Example call in a Dependency Service
if (MainApplication.ActivityContext!= null)
{
versionNumber = MainApplication.ActivityContext
.PackageManager
.GetPackageInfo(MainApplication.ActivityContext.PackageName, 0)
.VersionName;
}
Additional Resources
Android.App.Application.IActivityLifecycleCallbacks
registerActivityLifecycleCallbacks
In the latest scaffold of a new Xamarin Forms solution the CrossActivityPlugin (https://github.com/jamesmontemagno/CurrentActivityPlugin) is referenced in the Android project. So you can use
CrossCurrentActivity.Current.Activity.StartActivity(myIntent)
I have a desktop application that will be used on computers with no keyboard, input will be on a touch screen. I can get the virtual keyboard to show up on textfields fine when running from eclipse. I used these arguments
-Dcom.sun.javafx.touch=true
-Dcom.sun.javafx.isEmbedded=true
-Dcom.sun.javafx.virtualKeyboard=none
The following link shows me where to add the arguments.
how to add command line parameters when running java code in Eclipse?
When I make a runnable jar file the keyboard does not show up. I am looking for a way to set these arguments programmatically so that the runnable jar file will display the virtual keyboard on any computer. Any help will be greatly appreciated.
The only working solution I could find came from here
Gradle build for javafx application: Virtual Keyboard is not working due to missing System property
Create a wrapper class and set the system properties before invoking the applications original main method.
public class MainWrapper {
public static void main(String[] args) throws Exception
{ // application - package name
Class<?> app = Class.forName("application.Main");
Method main = app.getDeclaredMethod("main", String[].class);
System.setProperty("com.sun.javafx.isEmbedded", "true");
System.setProperty("com.sun.javafx.touch", "true");
System.setProperty("com.sun.javafx.virtualKeyboard", "javafx");
Object[] arguments = new Object[]{args};
main.invoke(null, arguments);
}
}
When making the runnable jar file just point to the MainWrapper class for the launch configuration.
The -D option to the JVM sets a system property. So you can achieve the same by doing the following:
public class MyApplication extends Application {
#Override
public void init() {
System.setProperty("com.sun.javafx.touch", "true");
System.setProperty("com.sun.javafx.isEmbedded", "true");
System.setProperty("com.sun.javafx.virtualKeyboard", "none");
}
#Override
public void start(Stage primaryStage) {
// ...
}
}
I have my MainActivity and inside that I have a number of fragments. I also have another activity that works as my launcher and does everything to do with the Google Drive section of my app. On start up this activity launches, connects to Drive and then launches the MainActivity. I have a button in one of my fragments that, when pushed, needs to call a method in the DriveActivity. I can't create a new instance of DriveActivity because then googleApiClient will be null. Is this possible and how would I go about doing it? I've already tried using getActivity and casting but I'm assuming that isn't working because DriveActivity isn't the fragments parent.
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//TODO for test only remove
directory = new Directory(SDCARD + LOCAL_STORAGE);
byte[] zippedFile = directory.getZippedFile(SDCARD + STORAGE_LOCATION + directory.getZipFileName());
//Here I need to somehow call DriveActivity.uploadFileToDrive(zippedFile);
//((DriveActivity)getActivity()).uploadFileToDrive(zippedFile);
}
});
Right, so I'm having a bit of difficulty with the heirarchy but I think what you want to do is define a method in the fragment that the activity will be required to override to use.
This will allow you to press the button, and then fire a method whos actual implementation is inside the parent.
public interface Callbacks {
/**
* Callback for when an item has been selected.
*/
public void onItemSelected(String id);
}
example implementation:
private static Callbacks sDummyCallbacks = new Callbacks() {
#Override
public void onItemSelected(String id) {
//Button fired logic
}
};
so in the child you'd do just call:
this.onItemSelected("ID of Class");
EDITED
In retrospect what I believe you need is an activity whos sole purpose is to upload files, not fire off other activities.
Heres an example of a 'create file' activity:Google Demo for creating a file on drive
Heres an example of the 'base upload' activity' Base Service creator