如何使用io调度程序使用mockito,rxjava2在spring mvc中测试服务?

时间:2017-07-21 00:57:29

标签: spring-boot mockito junit4 rx-java2

我正在尝试测试一个使用rxjava2,flatmap和io调度程序的服务方法。尽管似乎运行,但测试似乎没有使用mockito调用任何模拟方法。 observable永远不会返回一个对象。

如何使用多线程rxjava2代码测试spring服务?

以下是我正在测试的服务中的方法

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public io.reactivex.Observable<EntryStatistic> compute(final User user, final int startYear, final int endYear) {
    final GregorianCalendar calendarg = new GregorianCalendar();

    if (endYear < startYear)
        throw new IllegalArgumentException("endYear");
    if (endYear < 2003)
             throw new IllegalArgumentException("endYear");
    if (startYear < 2003)
        throw new IllegalArgumentException("startYear");

    return io.reactivex.Observable.range(startYear, endYear - startYear + 1)
            .flatMap(new Function<Integer, ObservableSource<EntryStatistic>>() {
                @Override
                public ObservableSource<EntryStatistic> apply(final Integer yr) throws Exception {
                    return io.reactivex.Observable.fromCallable(new Callable<EntryStatistic>() {
                        @Override
                        public EntryStatistic call() throws Exception {

                            log.debug("testing with year: " + yr + " user: " + user.getUsername() );
                            EntryStatistic es = entryStatisticRepository.findByUserAndYear(user, yr);
                            final long count = entryRepository.calendarCount(yr, user.getUsername());

                            if (es == null) {
                                log.trace("Creating new entry statistic");
                                es = new EntryStatistic();
                                es.setUser(user);
                            }

                            es.setCount(count);
                            es.setYear(yr);
                            es.setModified(calendarg.getTime());

                            log.trace("save and flush time");
                            return entryStatisticRepository.saveAndFlush(es);

                        }
                    });
                }
            }).subscribeOn(Schedulers.io());
}

这是测试代码:

@Test
public void computeSingle() {
    when(entryStatisticRepository.findByUserAndYear(user, TEST_YEAR + 1)).thenReturn(entryStatistic);
    when(user.getUsername()).thenReturn(TEST_USER);
    when(entryRepository.calendarCount(TEST_YEAR, TEST_USER)).thenReturn(1L);
    when(entryStatistic.getUser()).thenReturn(user);
    when(entryStatistic.getCount()).thenReturn(1L);
    when(entryStatistic.getYear()).thenReturn(TEST_YEAR);
    when(entryStatisticRepository.saveAndFlush(entryStatistic)).thenReturn(entryStatistic);

    TestObserver<EntryStatistic> testObserver = entryStatisticService.compute(user, TEST_YEAR, TEST_YEAR )
            .test();

    testObserver.awaitTerminalEvent();
    testObserver
        .assertNoErrors()
        .assertValue(new Predicate<EntryStatistic>() {
            @Override
            public boolean test(final EntryStatistic entryStatistic) throws Exception {
                return entryStatistic.getCount() == 1L ;
            }
        });

    verify(entryStatisticRepository, atLeastOnce()).findByUserAndYear(user, TEST_YEAR);
    verify(entryRepository, atLeastOnce()).calendarCount(TEST_YEAR, TEST_USER);
}

最后,我尝试将代码的单线程调度程序强制为junit规则。

public class TrampolineSchedulerRule implements TestRule {

@Override
public Statement apply(final Statement base, Description d) {
  return new Statement() {
    @Override
    public void evaluate() throws Throwable {
        RxJavaPlugins.setIoSchedulerHandler(new Function<Scheduler, Scheduler>() {
            @Override
            public Scheduler apply(final Scheduler scheduler) throws Exception {
                return Schedulers.trampoline();
            }
        });
        RxJavaPlugins.setComputationSchedulerHandler(new Function<Scheduler, Scheduler>() {
                         @Override
                         public Scheduler apply(final Scheduler scheduler) throws Exception {
                             return Schedulers.trampoline();
                         }
                     });
        RxJavaPlugins.setNewThreadSchedulerHandler(new Function<Scheduler, Scheduler>() {
                                      @Override
                                      public Scheduler apply(final Scheduler scheduler) throws Exception {
                                          return Schedulers.trampoline();
                                      }
                                  });


      try {
        base.evaluate();
      } finally {
        RxJavaPlugins.reset();
      }
    }
  };
}
}

问题是没有返回任何对象。我看到了错误

  

java.lang.AssertionError:没有值(latch = 0,values = 0,errors =   0,完成= 1)

1 个答案:

答案 0 :(得分:3)

有关如何设置模拟的问题。这个模拟的电话

 when(entryStatisticRepository.saveAndFlush(entryStatistic)).thenReturn(entryStatistic);

期望将entryStatistic对象传递给saveAndFlush,但实际上新的EntryStatistic对象将传递给saveAndFlush,并且不会返回所需的模拟值。这意味着从Callable返回空值。由于null中的rx-java2值无法在flatMap中发出,导致when(entryStatisticRepository.findByUserAndYear(user, TEST_YEAR+1)).thenReturn(entryStatistic); 没有发出任何值,并且您会立即收到没有值的值。

可以通过删除加1来修复测试(因为范围只会发出一个等于TEST_YEAR的值)

flatMap

一个有趣的问题是为什么Observable.range(0, 10).flatMap(i -> Observable.fromCallable(() -> null)) 没有返回任何项目并成功完成

Observable.fromCallable(() -> null)

并以

的错误完成
Observable.range(0, 10).flatMap(i -> Observable.error(new RuntimeException()))

也完成了

的错误
flatMap

我想这就是rx开发人员决定如何处理public static void shareViaMail(Activity activity, String title, String body, String filePath) { Uri URI = Uri.parse("file://" + filePath); final Intent emailIntent = new Intent(Intent.ACTION_VIEW); emailIntent.setData(Uri.parse("mailto:")); emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{"contact@brightsociety.com"}); if (URI != null) { emailIntent.putExtra(Intent.EXTRA_STREAM, URI); } try { activity.startActivity(emailIntent); } catch (Exception e) { ((BaseActivity) activity).showToast("Gmail App is not installed"); e.printStackTrace(); } } 中的空值,这里我们正在处理一些不应该被使用的东西(编辑:确认是一个错误,请参阅注释)