What I'm doing: I'm testing an Android aplication using Robotium.
What works: If I have a test in one test method everithing works fine
What doesn't work: If I try do divide this test in to two smaller, then the first test is passing, and the second is hanging (or freezing - I don't know how to name that)
Why I need that I need that to make reports in sppon showing bars for each test (I will have: testLogin, testAddCustomer, testLogout etc). Example of spoon report looks like this:
If i have one big test (testAll) there is only one big green bar, but I need to have many short bars for each test method like on the image above.
What I've done: I have read a lot of different topics about simillar problems but it didn't help me
Here is an short example of what works (one method - testAll()) I wrote currents activities in comments:
public class LogInLogOut extends ActivityInstrumentationTestCase2 {
private Solo solo;
private static final String LAUNCHER_ACTIVITY_FULL_CLASSNAME = "mobile.touch.core.activity.SplashScreenActivity";
private static Class<?> launcherActivityClass;
static {
try {
launcherActivityClass = Class.forName(LAUNCHER_ACTIVITY_FULL_CLASSNAME);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public LogInLogOut() throws ClassNotFoundException {
super(launcherActivityClass);
}
public void setUp() throws Exception {
super.setUp();
solo = new Solo(getInstrumentation());
getActivity();
}
#Override
protected void tearDown() throws Exception {
try {
solo.finalize();
} catch (Throwable e) {
e.printStackTrace();
}
getActivity().finish();
super.tearDown();
}
public void testAll() {
// here is LoginActivity <<-----
// username
solo.clickOnView(solo.getView(0x3));
solo.enterText((android.widget.EditText) solo.getView(0x3), "user");
// enter password
solo.clickOnView(solo.getView(0x3, 1));
solo.enterText((android.widget.EditText) solo.getView(0x3, 1), "password");
// click on log in button
solo.clickOnView(solo.getView(android.widget.Button.class, 0));
// here ContainerActivity starts <<-----
//click on log out
solo.clickOnMenuItem("LogOut");
}
}
With the testAll () method all test is passed. But I need to divide that into testLogin() and test testLogout().
here is how I divide metod testAll into two smaller (testLogin() & testLogout()):
public void testLogin() {
// here is LoginActivity <<-----
// username
solo.clickOnView(solo.getView(0x3));
solo.enterText((android.widget.EditText) solo.getView(0x3), "user");
// enter password
solo.clickOnView(solo.getView(0x3, 1));
solo.enterText((android.widget.EditText) solo.getView(0x3, 1), "password");
// click on log in button
solo.clickOnView(solo.getView(android.widget.Button.class, 0));
// here ContainerActivity starts <<-----
}
public void testOut() {
//click on log out
solo.clickOnMenuItem("LogOut");
}
Now first test (testLogin()) is passed and the second (testLogout()) is hanging
To check if the test even started I put log into it.
public void testOut() {
Log.i("checkTestB", "test B started"); <<-- here is the log
//click on log out
solo.clickOnMenuItem("LogOut");
}
It occurse that the testLog is not ececuting the code, becouse "test B started" was not in the log
Question: How can I solve that problem?
I find answer. Method setUp() is perormed before each test method, and tearDown() is performed after each test method. It coused the problem.
I override setUp() and tearDown() with:
#Override
public void setUp() {
}
#Override
protected void tearDown() throws Exception {
if (exit == true) {
try {
solo.finalize();
} catch (Throwable e) {
e.printStackTrace();
}
solo.finishOpenedActivities();
super.tearDown();
}
}
Then I wrote my own mehtods startUp() identical like setUp() and tearDown() deleted.
public void startUp() {
solo = new Solo(getInstrumentation());
getActivity();
}
Then setUp() do nothig when it is performed before test. The same thing is with teadDown() it makes nothing unltil I will set exit==true in test method.
The resolution is to start manulay startUp() in FIRST test method and in the LAST test method exit=ture (default is false), then tearDown() will run and close everything.
Now I manually start activity in testLogin():
public void testLogin() {
startUp(); <<<<<<<-----------------same method as setUp() but can started manually
solo.clickOnView(solo.getView(0x3));
solo.enterText((android.widget.EditText) solo.getView(0x3), "user");
solo.clickOnView(solo.getView(0x3, 1));
solo.enterText((android.widget.EditText) solo.getView(0x3, 1), "password");
solo.clickOnView(solo.getView(android.widget.Button.class, 0));
}
In the second methods testLogout() I use on the begginig: solo = new Solo(getInstrumentation()); and turnDown() method on the end
public void testLogOut() {
solo = new Solo(getInstrumentation());
solo.clickOnMenuItem("LogOut");
exit=true;
}
Related
After running this,
App run.
TestMethod1() executed.
App closed.
TestMethod2() executed.
But I don't want the app to be closed (Step3). I want TestMethod2() to be executed after TestMethod1() is done:
App run.
TestMethod1() execute.
TestMethod2() execute.
App close.
--> I also tried with AddAdditionalCapability("NoReset, true") and AddAdditionalCapability("FullReset, False"), which didn't work.
=> I am using Appium.WebDriver(4.3.1) and C#.
[TestClass]
public class Walkthrough
{
private string _appPath = #"PathToMyApp\MyApp.apk";
private AppiumDriver<AndroidElement> _driver;
[TestInitialize]
public void Setup()
{
var appiumOption = new AppiumOptions();
appiumOption.AddAdditionalCapability(MobileCapabilityType.App, _appPath);
appiumOption.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Android");
appiumOption.AddAdditionalCapability(MobileCapabilityType.DeviceName, "Pixel 4");
appiumOption.AddAdditionalCapability(MobileCapabilityType.PlatformVersion, "12");
appiumOption.AddAdditionalCapability(MobileCapabilityType.Udid, "99171FFAZ000GE");
appiumOption.AddAdditionalCapability(MobileCapabilityType.NoReset, true);
_driver= new AndroidDriver<AndroidElement>(new Uri("http://127.0.0.1:4723/wd/hub"), appiumOption);
}
[TestCleanup]
public void TestCleanup()
{
_driver.CloseApp();
}
[TestMethod]
public void TestMethod1()
{
_driver.FindElement(By.ID("ELEMENT1")).Click();
}
[TestMethod]
public void TestMethod2()
{
_driver.FindElement(By.ID("ELEMENT2")).Click();
}
}
I finally solved the problem by adding [ClassInitialize] and TestContext in Setup method.
Make sure your method is "Static".
[ClassInitialize]
public static void Setup(TestContext testContext)
{
{
var appiumOption = new AppiumOptions();
appiumOption.AddAdditionalCapability(MobileCapabilityType.App, _appPath);
appiumOption.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Android");
appiumOption.AddAdditionalCapability(MobileCapabilityType.DeviceName, "Pixel 4");
appiumOption.AddAdditionalCapability(MobileCapabilityType.PlatformVersion, "12");
appiumOption.AddAdditionalCapability(MobileCapabilityType.Udid, "99171FFAZ000GE");
appiumOption.AddAdditionalCapability(MobileCapabilityType.NoReset, true);
_driver= new AndroidDriver<AndroidElement>(new Uri("http://127.0.0.1:4723/wd/hub"), appiumOption);
}
}
I have a problem when i try to go back from a cordova plugin integrated on cordova. When i try to do it, the app crashes, I debugged first the javascript and then the native code that javascript call and this is the instruction that make app crash.
cordova.getActivity().finish();
This is in TestPlugin.java file in this most global context:
if (action.equals("open")) {
try {
cordova.getThreadPool().execute(new Runnable() {
#Override
public void run() {
Intent intent = new Intent(cordova.getActivity(),
PhemiumEnduserActivity.class);
cordova.getActivity().startActivity(intent);
cordova.getActivity().overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
});
callback.success("");
} catch (final Exception e) {
callback.error(e.getMessage());
}
}
else if( action.equals("exit_app") ) {
try {
cordova.getThreadPool().execute(new Runnable() {
#Override
public void run() {
cordova.getActivity().finish();
}
});
callback.success("");
} catch (final Exception e) {
callback.error(e.getMessage());
}
}
When the app calls the plugin executes the "open" part and when i click the back button makes the "exit_app" part and then on cordova.getActivity().finish(); app crashes with no error on the android studio console. There is no signal of webview close. What i'm doing wrong? Why it crashes?
Please post full plugin code or repository, but i think is because you try to finish activity in another thread, or activity no more exist (getActivity return null).
You need to try with somewhat like this:
protected Activity mActivity;
....
else if( action.equals("exit_app") ) {
mActivity = cordova.getActivity();
try {
cordova.getThreadPool().execute(new Runnable() {
#Override
public void run() {
if(mActivity!=null)
mActivity.finish();
}
});
callback.success("");
} catch (final Exception e) {
callback.error(e.getMessage());
}
}
For more trace info try to open android project in Android studio and debug it.
I am writing a test for a ViewModel. The function in the ViewModel is this:
public void discoverMovies(boolean showLoading) {
// reset the states to initial states
moviesLoading.set(showLoading);
errorViewShowing.set(false);
emptyViewShowing.set(false);
mMoviesRepository.getPopularMovies(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribeWith(new DisposableObserver<List<Movie>>() {
#Override
public void onNext(List<Movie> value) {
// show or hide empty view
boolean isEmpty = value == null || value.isEmpty();
if (!isEmpty) {
saveResponse(value);
movies.clear();
movies.addAll(value);
}
emptyViewShowing.set(isEmpty);
}
#Override
public void onError(Throwable throwable) {
errorViewShowing.set(true);
moviesLoading.set(false);
emptyViewShowing.set(false);
errorString.set(getErrorMessage(throwable));
}
#Override
public void onComplete() {
moviesLoading.set(false);
errorViewShowing.set(false);
}
});
}
private void saveResponse(final MovieResponse mainResponse) {
Realm.getDefaultInstance().executeTransaction(new Realm.Transaction() {
#Override public void execute(Realm realm) {
RealmMovie realmMovie = realm.createObject(RealmMovie.class);
realmMovie.setId(1);
realmMovie.setMarvelResponse(new Gson().toJson(mainResponse));
}
});
}
And I test the function above in my test class like this:
Note: Everything works without the Realm aspect. I've confirmed that.
#Test
public void getPopularMoviesWithoutError() {
// given the following movies
when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(Observable.just(MOVIES));
// discover popular movies
mMoviesViewModel.discoverMovies(true);
// verify that the repository is called
verify(mMoviesRepository).getPopularMovies(PAGE);
// test that the loading indicator is hidden
assertFalse(mMoviesViewModel.moviesLoading.get());
// check that the empty view is hidden
assertFalse(mMoviesViewModel.emptyViewShowing.get());
// check that the error view is hidden
assertFalse(mMoviesViewModel.errorViewShowing.get());
assertTrue(mMoviesViewModel.movies.size() == MOVIES.size());
}
And it keeps on giving me java.lang.IllegalStateException: CallRealm.init(Context)before calling this method. How can I initialize Realm
to be available
I think the error message you are getting is quite clear about what is causing the problem. You are not calling Realm.init.
There are several ways of doing this. The simplest is the #Before and #After annotations on the test suite. You could also use a TestRule
Unfortunately, Realm.init requires a Context. To get that context, you are going to have to be in some environment that has one. That means that you will either have to run your tests on a device, as Instrumentation tests or, as #David Rawson suggests, use Robolectric.
I want to be able to have Espresso monitor Picasso as an IdlingResource so that I can run ViewMatchers once the image has been successfully loaded.
From navigating through the Picasso source code, I don't see why this isn't working. Here's what I tried:
Picasso picasso = new Picasso.Builder(context).build();
Field dispatcherField = Picasso.class.getDeclaredField("dispatcher");
dispatcherField.setAccessible(true);
try {
Dispatcher dispatcher = (Dispatcher) dispatcherField.get(picasso);
Espresso.registerLooperAsIdlingResource(dispatcher.dispatcherThread.getLooper());
} catch (NoSuchFieldException e) {
throw new PicassoHasBeenRefactoredException();
} catch (Exception e) {
e.printStackTrace();
}
onView(withId(R.id.image_view)).check(matches(withImage(R.drawable.drawable)));
(yes, I know, reflecting is icky, but I couldn't find another way of getting a handle on the Looper)
But it results in this error when trying to get the Bitmap from the ImageView:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.graphics.Bitmap android.graphics.drawable.BitmapDrawable.getBitmap()' on a null object reference
To check that the test is running as expected once an image has been loaded, I tried introducing a Thread.sleep(1000) in lieu of the IdlingResource check and it passed.
Is it safe to assume that the IdlingResource hasn't been set up correctly, and, more importantly, what would be the correct way of waiting for Picasso to finish loading before checking views with Espresso?
I'm using an IdlingResource that checks if there are actions left.
Note that the IdlingResource must live in the same package as Picasso to gain access to a package-protected variable
package com.squareup.picasso;
public class PicassoIdlingResource implements IdlingResource, ActivityLifecycleCallback {
protected ResourceCallback callback;
WeakReference<Picasso> picassoWeakReference;
#Override
public String getName() {
return "PicassoIdlingResource";
}
#Override
public boolean isIdleNow() {
if (isIdle()) {
notifyDone();
return true;
} else {
return false;
}
}
public boolean isIdle() {
return picassoWeakReference == null
|| picassoWeakReference.get() == null
|| picassoWeakReference.get().targetToAction.isEmpty();
}
#Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.callback = resourceCallback;
}
void notifyDone() {
if (callback != null) {
callback.onTransitionToIdle();
}
}
#Override
public void onActivityLifecycleChanged(Activity activity, Stage stage) {
switch (stage) {
case CREATED:
picassoWeakReference = new WeakReference<>(Picasso.with(activity));
break;
case STOPPED:
// Clean up reference
picassoWeakReference = null;
break;
default: // NOP
}
}
}
I don't think using WeakReference is needed, but it doesn't hurt either.
Also, I've identified one case where it doesn't wait until Picasso finishes (when using .load(null)). So, use at your own risk and please come back if you improve it.
See gist for full details and usage (https://gist.github.com/Maragues/0c0db81a137c8d067396)
I'm trying to make what I thought would be a simple call to a google app engine project.
From a unit test, the call works fine if I access the api directly. I can't do this however, since the call is blocking, so it has to be run from an async task.
What seems to happen is:
A unit test inheriting from ActivityInstrumentationTestCase2 sets up the activity and populates some values
Then I click the button to make the request:
final Button mBet = (Button) mActivity.findViewById(R.id.mybutton);
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
mName.setText("blah");
mBet.performClick();
}
});
Then this calls the underlying code to start an async task:
public void bet() throws IOException, InterruptedException {
mMakeTask = new AsyncTask<Void, Void, Model>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
mBetWait = new ProgressDialog(getActivity());
mBetWait.setCancelable(true);
mBetWait.show();
}
#Override
protected Model doInBackground(Void... voids) {
try {
mModel = mController.makeBet(mModel.getBetName(), mModel.getDescription(), mModel.getAnswer(), mModel.getAgainst());
} catch (IOException e) {
e.printStackTrace();
}
return mModel;
}
#Override
protected void onPostExecute(BetModel model) {
super.onPostExecute(model);
Context context = getActivity();
Intent intent = new Intent(context, OtherActivity.class);
intent.putExtra("bet", mModel);
mBetWait.dismiss();
context.startActivity(intent);
}
};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mMakeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
mMakeTask.execute();
}
}
And I forgot to add... when controller is called, it boils down to an appengine call:
public BetInfo makeBet(BetRequest info) throws IOException {
Betrequestendpoint.Builder endpointBuilder = new Betrequestendpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) { }
});
Betrequestendpoint endpoint = CloudEndpointUtils.updateBuilder(
endpointBuilder).build();
BetRequest result = endpoint.insertBetRequest(info).execute();
BetInfo resultInfo = new BetInfo();
resultInfo.setName(result.getName());
resultInfo.setDescription(result.getDescription());
resultInfo.setId(result.getId());
return resultInfo;
}
And to make this more odd... the request call appears to be blocked from the client with the stack trace:
"<13> AsyncTask #2"#831,912,607,944 in group "main": RUNNING
toUpperCase():3266, Character {java.lang}
toUpperCase():3251, Character {java.lang}
toUpperCase():162, CaseMapper {java.lang}
toUpperCase():1548, String {java.lang}
initServiceInfo():152, Services {org.apache.harmony.security.fortress}
getCacheVersion():211, Services {org.apache.harmony.security.fortress}
getInstance():137, Engine {org.apache.harmony.security.fortress}
getInstance():77, KeyManagerFactory {javax.net.ssl}
createDefaultKeyManager():362, SSLParametersImpl {com.android.org.conscrypt}
getDefaultKeyManager():355, SSLParametersImpl {com.android.org.conscrypt}
<init>():111, SSLParametersImpl {com.android.org.conscrypt}
getDefault():146, SSLParametersImpl {com.android.org.conscrypt}
<init>():34, OpenSSLSocketFactoryImpl {com.android.org.conscrypt}
newInstanceImpl():-1, Class {java.lang}
newInstance():1208, Class {java.lang}
getDefault():56, SSLSocketFactory {javax.net.ssl}
<clinit>():114, HttpsURLConnection$NoPreloadHolder {javax.net.ssl}
getDefaultSSLSocketFactory():163, HttpsURLConnection {javax.net.ssl}
copyWithDefaults():363, OkHttpClient {com.android.okhttp}
open():345, OkHttpClient {com.android.okhttp}
open():340, OkHttpClient {com.android.okhttp}
openConnection():28, HttpHandler {com.android.okhttp}
openConnection():479, URL {java.net}
buildRequest():133, NetHttpTransport {com.google.api.client.http.javanet}
buildRequest():68, NetHttpTransport {com.google.api.client.http.javanet}
execute():858, HttpRequest {com.google.api.client.http}
executeUnparsed():410, AbstractGoogleClientRequest {com.google.api.client.googleapis.services}
executeUnparsed():343, AbstractGoogleClientRequest {com.google.api.client.googleapis.services}
execute():460, AbstractGoogleClientRequest {com.google.api.client.googleapis.services}
makeBet():36, BetController {com.chillypixel.youwereright.controller}
makeBet():36, BetApiController {com.chillypixel.youwereright.controller}
doInBackground():129, BetFragment$1 {com.chillypixel.youwereright.bet}
doInBackground():117, BetFragment$1 {com.chillypixel.youwereright.bet}
call():288, AsyncTask$2 {android.os}
run():237, FutureTask {java.util.concurrent}
runWorker():1112, ThreadPoolExecutor {java.util.concurrent}
run():587, ThreadPoolExecutor$Worker {java.util.concurrent}
run():841, Thread {java.lang}
What seems to happen, is I run the test, and it gets to where the progress dialog pops up.
If I then manually interact with the app in the emulator (and hit back or something) the progress dialog goes away and the call completes - then the test passes.
If I just leave it alone though, it appears that the progress dialog just happily spins forever.
Any help would be greatly appreciated.