java - rxjava 中的异步测试不起作用

标签 java android unit-testing junit rx-java

我正在尝试通过以下方式对我的用例进行单元测试:

@RunWith(MockitoJUnitRunner.class)
public class DeviceCheckInUseCaseTest extends InstrumentationTestCase {

    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    ShopRepository shopRepository;

    @Mock
    UserRepository userRepository;

    @Mock
    SettingRepository settingRepository;

    private DeviceCheckInUseCase deviceCheckInUseCase;

    private PublishSubject<CheckInResult> checkinSubject;

    private static final String SHOPID = "shop id";
    private static final String MEMBERID = "member id";

    private TestSubscriber<CheckInResult> testSubscriber;

    @Mock
    Activity activity;

    @Before
    public void setUp() {

        testSubscriber = new TestSubscriber<>();
        checkinSubject = PublishSubject.create();

        when(shopRepository
            .checkIn(
                activity,
                SHOPID,
                MEMBERID,
                -60
            )
        ).thenReturn(checkinSubject.asObservable());

        when(settingRepository.getCheckinRssiThreshold()).thenReturn(-60);

        deviceCheckInUseCase = new DeviceCheckInUseCase(userRepository, shopRepository, settingRepository);
    }

    @Test
    public void testCheckInSucceeded() {
        final ArrayList<CheckInResult> succeeded = new ArrayList<CheckInResult>();
        final ArrayList<Throwable> failed = new ArrayList<Throwable>();

        this.deviceCheckInUseCase.checkIn(activity, SHOPID, MEMBERID)
            .doOnNext(new Action1<CheckInResult>() {
                @Override
                public void call(CheckInResult checkInResult) {
                    succeeded.add(checkInResult);
                }
            })
            .doOnError(new Action1<Throwable>() {
                @Override
                public void call(Throwable throwable) {
                    failed.add(throwable);
                }
            })
            .subscribe(testSubscriber)
        ;

        testSubscriber.assertValueCount(0);
        assertEquals(0, succeeded.size());
        assertEquals(0, failed.size());

        checkinSubject.onNext(new CheckInSuccess());

        this.testSubscriber.assertValueCount(1);
        assertEquals(1, succeeded.size());
        assertEquals(0, failed.size());
    }
}

下面是我的DeviceCheckInUseCase.java

public class DeviceCheckInUseCase {

    private UserRepository userRepository;
    private ShopRepository shopRepository;
    private SettingRepository settingRepository;
//    private Subscription


    @Inject
    public DeviceCheckInUseCase(UserRepository userRepository, ShopRepository shopRepository, SettingRepository settingRepository) {
        this.userRepository = userRepository;
        this.shopRepository = shopRepository;
        this.settingRepository = settingRepository;
    }

    public Observable<CheckInResult> checkIn(final Activity context, final String shopId, final String memberId) {

        return Observable.create(new Observable.OnSubscribe<CheckInResult>() {
            @Override
            public void call(final Subscriber<? super CheckInResult> subscriber) {

                final Subscription subscription = new BooleanSubscription();

                shopRepository
                    .checkIn(context, shopId, memberId, settingRepository.getCheckinRssiThreshold())
                    .retryWhen(new Func1<Observable<? extends Throwable>, Observable<Integer>>() {

                        @Override
                        public Observable<Integer> call(Observable<? extends Throwable> failure) {
                            return failure.flatMap(new Func1<Throwable, Observable<Integer>>() {

                                @Override
                                public Observable<Integer> call(Throwable result) {

                                    if (result instanceof BluetoothFailure) {
                                        return Observable.error(result);
                                    } else if (!subscription.isUnsubscribed()) {
                                        return Observable.just(0);
                                    } else {
                                        return Observable.error(result);
                                    }
                                }

                            });
                        }
                    })
                    .flatMap(new Func1<CheckInResult, Observable<CheckInResult>>() {
                        @Override
                        public Observable<CheckInResult> call(final CheckInResult checkInResult) {
                            return userRepository.checkin()
                                    .map(new Func1<Void, CheckInResult>() {
                                        @Override
                                        public CheckInResult call(Void aVoid) {
                                            return checkInResult;
                                        }
                                    });
                        }
                    })
                    .subscribe(subscriber);
            }
        });
    }

    public boolean enableCheckin() {
        return userRepository.enableCheckin();
    }
}

如您所见,我期望 succeeded 数组增加一个元素,但其大小仍为空。

我一直试图找出为什么会发生这种情况,遇到了使用testschedulers和testsubscribers进行异步测试,但我认为我没有正确使用它:

@Before中我添加:

subscriber = new TestSubscriber<>();
checkinSubject.subscribe(subscriber);

并将@Test部分中的断言更改为:

this.subscriber.assertValueCount(0);
assertEquals(0, succeeded.size());
assertEquals(0, failed.size());

this.checkinSubject.onNext(new CheckInSuccess());

this.subscriber.assertValueCount(1);
assertEquals(1, succeeded.size());
assertEquals(0, failed.size());

assertValueCount 工作正常,但succeeded 仍然保持不变。如何让成功的断言发挥作用?

最佳答案

您的订阅链已损坏。您应该在下游而不是上游订阅。因此,在 doOnError 之后订阅,而不是直接在 Subject 上订阅。

this.deviceCheckInUseCase.checkIn()
    .doOnNext(succeeded::add)
    .doOnError(failed::add)
    .subscribe(subscriber);

以下是完整的测试代码:

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;

import org.junit.Before;
import org.junit.Test;

import rx.Observable;
import rx.Subscription;
import rx.observers.TestSubscriber;
import rx.subjects.PublishSubject;
import rx.subscriptions.BooleanSubscription;

public class Q47049714 {

  public static class BluetoothFailure extends Exception {
  }

  public static class CheckInSuccess implements CheckInResult {
  }

  public static interface CheckInResult {
  }

  public class ShopRepository {
    public Observable<CheckInResult> checkIn() {
      return checkinSubject;
    }
  }

  public class UserRepository {
    public Observable<Void> checkIn() {
      return Observable.just(null);
    }
  }

  public class DeviceCheckInUseCase {
    ShopRepository shopRepository = new ShopRepository();
    UserRepository userRepository = new UserRepository();

    public Observable<CheckInResult> checkIn() {
      return Observable.create(subscriber -> {
        Subscription subscription = new BooleanSubscription();
        shopRepository
            .checkIn()
            .retryWhen(failure -> failure.flatMap(result -> {
              if (result instanceof BluetoothFailure) {
                return Observable.error(result);
              } else if (!subscription.isUnsubscribed()) {
                return Observable.just(0);
              } else {
                return Observable.error(result);
              }
            }))
            .flatMap(checkInResult -> userRepository.checkIn().map(v -> checkInResult))
            .subscribe(subscriber);
      });
    }
  }

  PublishSubject<CheckInResult> checkinSubject;
  DeviceCheckInUseCase deviceCheckInUseCase;
  TestSubscriber<CheckInResult> subscriber;

  @Before
  public void setUp() {
    checkinSubject = PublishSubject.create();
    subscriber = new TestSubscriber<>();
    deviceCheckInUseCase = new DeviceCheckInUseCase();
  }

  @Test
  public void testCheckInSucceeded() {
    final ArrayList<CheckInResult> succeeded = new ArrayList<>();
    final ArrayList<Throwable> failed = new ArrayList<>();

    deviceCheckInUseCase.checkIn()
        .doOnNext(succeeded::add)
        .doOnError(failed::add)
        .subscribe(subscriber);

    subscriber.assertValueCount(0);
    assertEquals(0, succeeded.size());
    assertEquals(0, failed.size());

    checkinSubject.onNext(new CheckInSuccess());

    subscriber.assertValueCount(1);
    assertEquals(1, succeeded.size());
    assertEquals(0, failed.size());
  }
}

关于java - rxjava 中的异步测试不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47049714/

相关文章:

java - KeyEvent 不适用于 JTextArea,但适用于包含 JTextArea 的 JFrame

java - 如何使用 apache poi 检查 .xlsx 文件中的单元格文本是否有删除线

java - 如何只为textView的一部分设置背景图片

c++ - 有没有办法模拟 QSqlQuery?

unit-testing - 在 Windows 上运行 spark 单元测试

java - Hibernate:修改条件 sqlrestriction 以使用大于或等于日期

android - Cordova问题无法添加平台android

android - 自定义 TabLayout 指示器。制作标签指示器圆角?

c# - 我应该在每个 TestMethod 中还是在 ClassInitialize 中设置模拟方法?

java - 如何从 Spring Condition 访问文件属性?