I have some unit tests using androidx Espresso Every time I run the test I get the following error.
androidx.test.espresso.IdlingResourceTimeoutException: Wait for [WorkTrackerIdlingResource] to become idle timed out
Why am I getting timed out?
Its timing out on this line of code in my unittests,
onView(withId(R.id.map_container)).check(matches(isDisplayed()));
Unit Test Class:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class CanvassFragmentTest extends TopLevelNavFragmentTest<CanvassFragment> {
#NonNull
#Override
protected TopLevelNavFragment getTargetTopLevelNavFragment(#NonNull MainActivity activity) {
return activity.mCanvassFragment;
}
#Test
public void mapContainer_isDisplayed() {
onView(withId(R.id.id1_container)).check(matches(isDisplayed()));
}
}
And the xml which has the view we are trying to test.
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.company.MapContainerFragment">
<FrameLayout
android:id="#+id/id1_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
.....
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
I've tried changing the idlepreferences so that the timeout is 5 minutes and I still get the error.
EDIT 1
as request I'm adding the code were I add onTransitionToIdle at the bottom of this class bellow
public class WaitForWorkRule implements TestRule {
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
WorkTrackerIdlingResource idler = new WorkTrackerIdlingResource();
//Espresso.registerIdlingResources(idler);
IdlingRegistry.getInstance().register(idler);
try {
base.evaluate();
} finally {
IdlingRegistry.getInstance().unregister(idler);
}
}
};
}
/**
* #hide visible for injection
*/
public static class WorkTrackerIdlingResource implements IdlingResource {
private static final String TAG = WorkTrackerIdlingResource.class.getSimpleName();
#Inject
WorkTracker mWorkTracker;
#Nullable
ResourceCallback mResourceCallback;
public WorkTrackerIdlingResource() {
getUiTestInjector().inject(this);
}
#Override
public String getName() {
return TAG;
}
#Override
public boolean isIdleNow() {
boolean idle = !mWorkTracker.isAnyoneWorking();
if (idle && mResourceCallback != null) mResourceCallback.onTransitionToIdle();
return idle;
}
#Override
public void registerIdleTransitionCallback(#Nullable ResourceCallback callback) {
mResourceCallback = callback;
}
}
}
And this is the code were we register it as a rule.
#Rule
public final RuleChain mRuleChain;
public UiTest(#NonNull Class<T> activityClass) {
mActivityTestRule = new ActivityTestRule<>(activityClass);
mRuleChain = createRuleChain();
}
#NonNull
private RuleChain createRuleChain() {
RuleChain chain = RuleChain.emptyRuleChain()
.around(new InjectionRule())
.around(new WaitForWorkRule());
if (loggedIn()) {
chain = chain.around(new LoggedInRule());
}
return chain.around(mActivityTestRule);
}
Here's a brief explanation why your test always time out: you set a test rule that registers an idling resource before test begins, then when your activity starts, the main thread becomes busy. Generally, the test will continue when the main thread becomes idle, but your idling resource now blocks it and does not call onTransitionToIdle. So no matter how much time you set, it will always time out.
I think your usage of idling resource here may be not useful or incorrect. If there's no use case for it, you can take the rule out and it should work fine.
Related
I have implemented Evernote Android Job in my android application through
implementation 'com.evernote:android-job:1.2.6'
And I have define as signleton to get instance I have initiated it in my Application class through
JobManager.create(this).addJobCreator(new CreatingJob());
And I have two classes which are
JOB CREATING CLASS
public class CreatingJob implements JobCreator {
#Nullable
#Override
public Job create(#NonNull String tag) {
switch (tag) {
case SyncMasterDataJOB.TAG:
return new SyncMasterDataJOB();
}
return null;
}
}
JOB CLASS
public class SyncMasterDataJOB extends Job {
public static final String TAG = "job_note_sync";
#NonNull
#Override
protected Result onRunJob(#NonNull Params params) {
//Doing my Task HERE
MyLog.ShowELog("JOB STARTED", "Job Has been Started");
MyToast.Lmsg(getContext(), "Job Has been Started");
return Result.SUCCESS;
}
public static void scheduleJob() {
Set<JobRequest> jobRequests = JobManager.instance().getAllJobRequestsForTag(SyncMasterDataJOB.TAG);
if (!jobRequests.isEmpty()) {
return;
}
new JobRequest.Builder(SyncMasterDataJOB.TAG)
.setPeriodic(MIN_INTERVAL, MIN_FLEX)
.build()
.schedule();
}
}
But the Problem is My onRunJob() method is never called. I am new to Android JOBS. Can anyone tell me where i am doing wrong?
I am Taking reference from here
Job creator class ->
public class CreateJob implements JobCreator {
private Context context;
public CreateJob(Context context){
this.context = context;
}
// Here we have to register each of the jobs...
#Nullable
#Override
public Job create(#NonNull String tag) {
switch (tag) {
case CurrentWeatherUpdateJob.TAG:
return new CurrentWeatherUpdateJob();
default:
return null;
}
}
}
this is where i am registering my JobCreator.
// To use StartingPoint Anywhere in our app
// else you have to Instantiate StartingPoint inside every Activities on create...
public class StartingPoint extends Application {
#Override
public void onCreate() {
super.onCreate();
// Create Job is a class that registers all the Jobs...
JobManager.create(this).addJobCreator(new CreateJob(getApplicationContext()));
}
}
This is your Jobs Subclass ->
public class CurrentWeatherUpdateJob extends Job {
public static final String TAG = "CurrentWeatherUpdateJob";
// Update Weather Data every 15 Minutes...
private static final int CURRENTWEATHERUPDATE_TIMEINTERVAL = 15 * 60 * 1000;
// Interface that provides Data...
private ApiInterface service;
// For Celcius - metric / Kelvin - imperial
private String UnitType = "metric";
public CurrentWeatherUpdateJob() {
service = APIClient.getRetrofit_Weather().create(ApiInterface.class);
}
private static void ScheduleJobEvery15Minutes() {
// Scheduling Job After every 15 minutes...
new JobRequest.Builder(TAG)
.setPeriodic(CURRENTWEATHERUPDATE_TIMEINTERVAL)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(true)
.setUpdateCurrent(true)
.build()
.schedule();
}
// implement your onRunJob method here
}
Call your ScheduleJobEvery15Minutes() method from your activity you want.
The Problem is not in your code, but in the Setting up of the Period of the Job. evernote only works with periodic Job >=15 Min, while you are using 1 minute as period for running the Job. see the Documentation of evenote-job as it's based on Job-scheduler, which has the same constraint for running periodic job.
private void schedulePeriodicJob() {
int jobId = new JobRequest.Builder(DemoSyncJob.TAG)
.setPeriodic(TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(5))
.build()
.schedule();
}
this is the code that they put on their Library Documentation. Please check this out. ->
Why can't an interval be smaller than 15 minutes for periodic jobs? This library is a subset of 3 different APIs. Since Android Nougat the minimum interval of periodic jobs is 15 minutes. Although pre Nougat devices support smaller intervals, the least common was chosen as minimum for this library so that periodic jobs run with the same frequency on all devices.
The JobScheduler with Android Nougat allows setting a smaller interval, but the value is silently adjusted and a warning is being logged. This library throws an exception instead, so that misbehaving jobs are caught early. You can read more about it here.
this is the code that works for me ->
where // Update Weather Data every 15 Minutes...
private static final int CURRENTWEATHERUPDATE_TIMEINTERVAL = 15 * 60 * 1000;
private static void ScheduleJobEvery15Minutes() {
// Scheduling Job After every 15 minutes...
new JobRequest.Builder(TAG)
.setPeriodic(CURRENTWEATHERUPDATE_TIMEINTERVAL)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(true)
.setUpdateCurrent(true)
.build()
.schedule();
}
EDIT -> Also check your Jobcreator class, you are returning null value like this,
public class CreatingJob implements JobCreator {
#Nullable
#Override
public Job create(#NonNull String tag) {
switch (tag) {
case SyncMasterDataJOB.TAG:
return new SyncMasterDataJOB();
}
return null;
}
change your code to this ->
public class CreatingJob implements JobCreator {
#Nullable
#Override
public Job create(#NonNull String tag) {
switch (tag) {
case SyncMasterDataJOB.TAG:
return new SyncMasterDataJOB();
case default:
return null;
}
}
I apologise if this is a bit too vague here, but I'm not allowed to post my whole actual code. All I can say is I have a problem running this test as a part of ./gradlew connectedAndroidTest
#RunWith(AndroidJUnit4.class)
#LargeTest
public class MobileAppSanityTest extends AbstractEspressoTest {
#Rule
public ActivityTestRule<MainActivity> mActivityRule =
new ClearPreferencesActivityTestRule<>(MainActivity.class, getFiles());
#Override
protected Context getContext() {
return mActivityRule.getActivity();
}
#BeforeClass
public static void beforeAll() {
RoboGuice.Util.reset();
}
#Test
public void test_SingleUserFlow() {
navigateSplashScreen();
logIn();
doSomethingElse();
}
}
What happens here is that when I run this test class on its own - it runs fine, but when I run it as a part of 'connectedAndroidTest' the activity is stopped right after 'navigateSplashScreen' and login cannot be performed.
Error I get is:
java.lang.RuntimeException: No activities found. Did you t to launch the activity by calling getActivity() or startActivitySync or similar?
I'm quite new to Espresso and Android in general, so it's a bit hard to wrap my head around this. Please let me know if you need more information. I'll try to provide it out if that's the case.
a jUnit TestCase looks differently; think one can only use Espresso in there.
#RunWith(AndroidJUnit4.class)
public class MainActivityTest extends TestCase {
/** Log Tag */
private static final String LOG_TAG = MainActivityTest.class.getSimpleName();
/** the Activity of the Target application */
private MainActivity mActivity;
#Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<MainActivity>(MainActivity.class) {
};
#Override
public void setUp() throws Exception {
super.setUp();
}
/* obtaining the context from the ActivityTestRule */
#Before
public void setUpTest() {
this.mActivity = this.mActivityRule.getActivity();
}
/* add Espresso code eg. here */
#Test
#UiThreadTest
public void navigateSplashScreen() {
}
#Override
public void tearDown() throws Exception {
super.tearDown();
}
}
I am new with tests.
I have something like next code and wish to cover it with unitTests using the Mockito:
public void doSomeJob(){
//some code before
getMvpView().execute(getObservable());
//some code after
}
private Observable<Boolean> getObservable(){
return Observable.create(new ObservableOnSubscribe<Boolean>() {
#Override
public void subscribe(#NonNull ObservableEmitter<Boolean> e) throws Exception {
Thread.sleep(5000);
e.onNext(true);
e.onComplete();
}
});
}
so questions:
how correct write test for getMvpView().execute(getObservable()); using Mokito?
how can i verify result of getObservable()?
If your private method is not a part of the interface, i.e. cannot be reached from outside the class, it's not something you should test (presumably it's not, since it's private). Mockito in turn doesn't provide mocking of private methods. Thereby you either need to change your interface (make this data available outside) or leave it without testing.
What you should test is the effect of calling the public methods of your class under test. If you do so you will be able to freely refactor the implementation details later, and your tests will still verify that your class works as expected.
I suppose that your code is part of a presenter implementation and the getMvpView() method returns a view interface:
public class MvpPresenterImpl {
private MvpView view;
public void doSomeJob(){
//some code before
getMvpView().execute(getObservable());
//some code after
}
public void attachView(MvpView view) {
this.view = view;
}
private MvpView getMvpView() {
return view;
}
private Observable<Boolean> getObservable(){
return Observable.create(new ObservableOnSubscribe<Boolean>() {
#Override
public void subscribe(#NonNull ObservableEmitter<Boolean> e) throws Exception {
Thread.sleep(5000);
e.onNext(true);
e.onComplete();
}
});
}
}
You can test the effect of doSomeJob() like so:
public class MvpPresenterImplTest {
private MvpPresenterImpl presenter;
private MvpView mockView;
#Before
public void setUp() throws Exception {
// Create a mock view instance so that we can verify method calls on it
mockView = mock(MvpView.class);
// Create our object under test, and set it up with the mock view
presenter = new MvpPresenterImpl();
presenter.attachView(mockView);
}
#Test
public void doSomeJob_callsExecuteOnViewWithCorrectObserver() throws Exception {
// What we want to test is the effect of invoking a public method.
presenter.doSomeJob();
// Verify that the execute method has been called by your class
// under test, and save the parameter for later.
ArgumentCaptor<Observable<Boolean>> paramCaptor =
ArgumentCaptor.<Observable<Boolean>>forClass((Class)Observable.class);
verify(mockView).execute(paramCaptor.capture());
// Get the actual observable that the execute method was called with.
Observable<Boolean> param = paramCaptor.getValue();
// Get a test observer so that we can check what our Observable emits
// (TestObserver is a built-in feature of RxJava, not Mockito.)
TestObserver<Boolean> test = param.test();
// Assert that the Observable behaves as expected
test.assertComplete();
test.assertResult(true);
}
}
I need to collect some data on my current app in order to analyse performance speed by checking the average ellapsed time during Activity start up. I would like to run a test battery where the activity is started 10, 100, 1000 and 5000 times. For each test, it should remain open for at least 10 seconds (time needed to collect all data that happens asynchronously). What I want is exactly this behaviour without having to write these many methods:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class TestStreamLoadingPerformance {
private static final long TIME_OUT = 2;
private static final long WAITING_TIME = 10000;
#Rule
public ActivityTestRule mActivityRule = new ActivityTestRule(HomepageActivity.class);
private ElapsedTimeIdlingResource mIdleRes = new ElapsedTimeIdlingResource(WAITING_TIME);
#Before
public void setUp() {
IdlingPolicies.setMasterPolicyTimeout(TIME_OUT, TimeUnit.HOURS);
IdlingPolicies.setIdlingResourceTimeout(TIME_OUT, TimeUnit.HOURS);
Espresso.registerIdlingResources(mIdleRes);
}
#After
public void tearDown() {
Espresso.unregisterIdlingResources(mIdleRes);
}
#Test
public void test01() {
}
#Test
public void test02() {
}
#Test
public void test03() {
}
#Test
public void test04() {
}
#Test
public void test05() {
}
#Test
public void test06() {
}
#Test
public void test07() {
}
#Test
public void test08() {
}
#Test
public void test09() {
}
}
With the help of #Be_negative comments, this blog post and this answer, I was able to solve the problem with the code below:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class TestStreamLoadingPerformance {
#Rule
public ActivityTestRule mActivityRule = new ActivityTestRule(HomepageActivity.class, false, false);
#Rule
public RepeatRule mRepeatRule = new RepeatRule();
#After
public void tearDown() {
closeActivity();
}
private void closeActivity() {
final int N = 10;
try {
for (int i = 0; i < N; i++) {
Espresso.pressBack();
}
} catch (NoActivityResumedException e) {
Log.e(TestStreamLoadingPerformance.class.getSimpleName(), "Unable to close activities", e);
}
}
#Test
#RepeatRule.Repeat(times = 10)
public void collectData() {
mActivityRule.launchActivity(null);
}
}
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class RepeatRule implements TestRule {
#Retention(RetentionPolicy.RUNTIME)
#Target({
java.lang.annotation.ElementType.METHOD
})
public #interface Repeat {
public abstract int times();
}
private static class RepeatStatement extends Statement {
private final int times;
private final Statement statement;
private RepeatStatement(int times, Statement statement) {
this.times = times;
this.statement = statement;
}
#Override
public void evaluate() throws Throwable {
for (int i = 0; i < times; i++) {
statement.evaluate();
}
}
}
#Override
public Statement apply(Statement statement, Description description) {
Statement result = statement;
Repeat repeat = description.getAnnotation(Repeat.class);
if (repeat != null) {
int times = repeat.times();
result = new RepeatStatement(times, statement);
}
return result;
}
}
The easiest (as in least amount of new code required) way to do this is to run the test as a parametrized test (annotate with an #RunWith(Parameterized.class) and add a method to provide 10 empty parameters). That way the framework will run the test 10 times.
This test would need to be the only test in the class, or better put all test methods should need to be run 10 times in the class.
Here is an example:
#RunWith(Parameterized.class)
public class RunTenTimes {
#Parameterized.Parameters
public static List<Object[]> data() {
return Arrays.asList(new Object[10][0]);
}
public RunTenTimes() {
}
#Test
public void runsTenTimes() {
System.out.println("run");
}
}
With the above, it is possible to even do it with a parameter-less constructor, but I'm not sure if the framework authors intended that, or if that will break in the future.
If you are implementing your own runner, then you could have the runner run the test 10 times. If you are using a third party runner, then with 4.7, you can use the new #Rule annotation and implement the MethodRule interface so that it takes the statement and executes it 10 times in a for loop. The current disadvantage of this approach is that #Before and #After get run only once. This will likely change in the next version of JUnit (the #Before will run after the #Rule), but regardless you will be acting on the same instance of the object (something that isn't true of the Parameterized runner). This assumes that whatever runner you are running the class with correctly recognizes the #Rule annotations. That is only the case if it is delegating to the JUnit runners.
If you are running with a custom runner that does not recognize the #Rule annotation, then you are really stuck with having to write your own runner that delegates appropriately to that Runner and runs it 10 times.
Note that there are other ways to potentially solve this (such as the Theories runner) but they all require a runner. Unfortunately JUnit does not currently support layers of runners. That is a runner that chains other runners.
I had a very similar issue and as a result I've created a library to run Android UI tests multiple times. Might be useful in your case: https://github.com/stepstone-tech/AndroidTestXRunner
I'm trying to test that the AutoCompleteTextView will show the items after some word will be typed. But there is a delay between typing and showing the popup. First i was using Thread.sleep() and it was working just fine. But I know that this approach isn't clear so I'm trying to accomplish it with IdlingResource. But it doesn't work for me. I literally read first 5 pages of Google responses, but either I don't understand how it should work, or I have some error in my code.
Here is the code:
static class AutocompleteShowIdlingResource implements IdlingResource {
private Activity activity;
private #IdRes int resId;
private ResourceCallback resourceCallback;
public AutocompleteShowIdlingResource(Activity activity, #IdRes int resId) {
this.activity = activity;
this.resId = resId;
}
#Override
public String getName() {
return this.getClass().getName() + resId;
}
#Override
public boolean isIdleNow() {
boolean idle = ((AutoCompleteTextView) activity.findViewById(resId)).getAdapter() != null;
Log.d(TAG, "isIdleNow: " + idle);
if (idle) {
resourceCallback.onTransitionToIdle();
}
return idle;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.resourceCallback = callback;
}
}
The test itself:
Activity activity = calibrationActivityRule.getActivity();
onView(withId(R.id.autocomplete_occupation)).perform(typeText("dok"));
IdlingResource idlingResource = new AutocompleteShowIdlingResource(activity, R.id.autocomplete_occupation);
Espresso.registerIdlingResources(idlingResource);
assertEquals(((AutoCompleteTextView) activity.findViewById(R.id.autocomplete_occupation)).getAdapter().getCount(), 3);
Espresso.unregisterIdlingResources(idlingResource);
But the test fails on java.lang.NullPointerException when trying to call getCount() on null adapter. The log is printing
isIdleNow: false
just once, which is quite strange.
There isn't much clear examples how to use IdlingResource, so maybe someone can make it clear for me. Thanks.
Your IdlingResource will have an effect only if you use it together with onView(...).check(...) or onData(...).check(...). Actually, the "magic" will happen in the check call - it's the place where Espresso waits until there are no running AsyncTasks or no blocking IdlingResources.
Now let's correct your code so that it works:
Activity activity = calibrationActivityRule.getActivity();
onView(withId(R.id.autocomplete_occupation)).perform(typeText("dok"));
IdlingResource idlingResource = new AutocompleteShowIdlingResource(activity, R.id.autocomplete_occupation);
try {
Espresso.registerIdlingResources(idlingResource);
//that's where Espresso will wait until the idling resource is idle
onData(anything()).inAdapter(withId(R.id.autocomplete_occupation)).check(matches(isDisplayed());
finally {
Espresso.unregisterIdlingResources(idlingResource);
}
assertEquals(((AutoCompleteTextView) activity.findViewById(R.id.autocomplete_occupation)).getAdapter().getCount(), 3);