PetarMarijanovic / RxActivityResult

Rx wrapper around Androids OnActivityResult
24 stars 8 forks source link

Incompatibility with AndroidX Unit Tests #6

Open guness opened 5 years ago

guness commented 5 years ago

Hello, due to the nature of AndroidX tests, the library flow does not fit well.

protected PublishSubject<Pair<Integer, ActivityResult>> resultSubject = PublishSubject.create();

  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    resultSubject.onNext(Pair.create(requestCode, new ActivityResult(resultCode, data)));
  }

  @Override
  public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
  }

  /** TODO: Write JavaDoc. */
  public Single<ActivityResult> start(final Intent intent) {
    int requestCode = RequestCodeGenerator.generate();

    startActivityForResult(intent, requestCode);

    return resultSubject
        .filter(isRequestCodeEqual(requestCode))
        .map(toActivityResult())
        .firstOrError();
  }

When running with setup: intending(hasComponent(ComponentName(context, AbcActivity::class.java.name))).respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, null)) library succeeds to start the activity however fails to return the result. I have debugged and found out why...

Contradicting to natural android runtime, on test env. the flow is following:

  1. start#startActivityForResult
  2. onActivityResult
  3. start#resultSubject.filter....

onActivityResult method is just called within startActivityForResult. and since the subject is PublishSubject, consumer had no chance to subscribe to receive result and when method returns with filtered chain, it is too late. Because nothing will be posted to subject anymore.

petarmarijanovicfive commented 5 years ago

Hi guness,

I agree that this may be a problem for writing tests and that it is contradicting the natural android runtime. But at the same time, I don't see the need for you to write your tests like this. Because you are actually writing tests for this library, and I don't think this is what you want. You want to test the behavior around this library which is actually your code.

I suggest that you mock your RxActivityResult object and make it return whatever you wish. The start(...) method is returning a simple Single<ActivityResult> which shouldn't be hard to mock.

guness commented 4 years ago

Well, on the other hand; this property is private on most of our classes, and I don't want to open it, just because it does not match default android behavior under test environment.

AlexTrotsenko commented 4 years ago

this property is private on most of our classes

@guness If you are using any kind of Injection e.g. via Dagger - simply inject mock instead of implementation.

But at the same time, I don't see the need for you to write your tests like this.

@petarmarijanovicfive perhaps t it might not make much sense to do it for Unit tests, but it would be nice to have it working in Integration tests.