Тест RxJava JUnit с Retrofit вызывает исключение NullPointerException

У меня есть вызов API retrofit2

public interface FeedService {
    @GET("/api/v1/feeds")
    Observable<FeedResponse> getFeeds(@Query("page") Integer pageNumber,
                                          @Query("cats") String categories,
                                          @Query("lang") String language);
}

У меня есть репозиторий, который использует этот API так:

public class FeedRepositoryImpl implements FeedRepository {
    private static FeedRepositoryImpl INSTANCE;
    private int FIRST_PAGE = 1;
    private FeedService feedService;

    public static FeedRepositoryImpl getFeedRepository(FeedService feedService) {
        if (INSTANCE == null) {
            INSTANCE = new FeedRepositoryImpl(feedService);
        }
        return INSTANCE;
    }

    @Override
    public Observable<Feed> getFeeds(final int pageNumber, final String categories, final String
            language) {
        return feedService.getFeeds(pageNumber, categories, language)
                .concatMap(new Function<FeedResponse, ObservableSource<? extends Feed>>() {
                    @Override
                    public ObservableSource<? extends Feed> apply(FeedResponse feedResponse) throws
                            Exception {
                        return Observable.fromIterable(feedResponse.feedsData());
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }
}

Теперь я пытаюсь написать тесты JUnit, которые проверяют, что FeedService's getFeeds() вызывается с теми же аргументами, которые используются для вызова FeedRepository.getFeeds().

Однако я получаю NullPointerException. Вот как выглядит мой тест -

public class FeedRepositoryImplTest {

    private static String CATEGORIES = "feeds?page=1&cats=top,yt,world,ent,ls,sp";
    private static String LANGUAGE = "lang=en";
    private static int PAGE_NUMBER = 1;

    @Rule
    public ImmediateSchedulersRule immediateSchedulersRule = new ImmediateSchedulersRule();
    @Mock
    FeedService feedService;
    FeedRepositoryImpl feedRepository;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        feedRepository = FeedRepositoryImpl.getFeedRepository(feedService);
    }

    @Test
    public void getFeeds() throws Exception {
        // arrange
        Observable<Feed> emptyFeed = Observable.empty();
        when(feedRepository.getFeed(PAGE_NUMBER, CATEGORIES, LANGUAGE))
                .thenReturn(emptyFeed);

        // act
        feedRepository.getFeed(PAGE_NUMBER, CATEGORIES, LANGUAGE);

        System.out.println("feedRepository.getFeeds(PAGE_NUMBER, CATEGORIES, LANGUAGE)");
        // assert
        verify(feedService).getFeeds(PAGE_NUMBER, CATEGORIES, LANGUAGE);
    }
}

Мой TestRule выглядит так -

public class ImmediateSchedulersRule implements TestRule {
    @Override
    public Statement apply(final Statement base, Description description) {
        return () -> {
                RxJavaPlugins.setIoSchedulerHandler((scheduler) -> {
                        return Schedulers.trampoline();
                });
                RxJavaPlugins.setComputationSchedulerHandler((scheduler) -> {
                        return Schedulers.trampoline();
                });
                RxJavaPlugins.setNewThreadSchedulerHandler((scheduler) -> {
                        return Schedulers.trampoline();
                });
                RxAndroidPlugins.setMainThreadSchedulerHandler((scheduler) -> {
                        return Schedulers.trampoline();
                });
                RxAndroidPlugins.setInitMainThreadSchedulerHandler((schedulerCallable) -> {
                        return Schedulers.trampoline();
                });
                base.evaluate();
        };
    }
}

Исключение выбрасывается в этой строке -

.concatMap(new Function<FeedResponse, ObservableSource<? extends Feed>>() { ... }

Что я делаю не так?


person yadav_vi    schedule 22.02.2017    source источник


Ответы (1)


Вы пытаетесь mock использовать метод FoodRepositoryImpl, который является реальным объектом (используя издевательский feedService).

сдача:

feedRepository = FeedRepositoryImpl.getFeedRepository(feedService);

to

feedRepository = Mockito.spy(FeedRepositoryImpl.getFeedRepository(feedService));

РЕДАКТИРОВАТЬ:

Вы должны просто изменить макет метода на (шпионаж FeedRepositoryImpl не имеет значения):

when(feedService.getFeeds(PAGE_NUMBER, CATEGORIES, LANGUAGE)).thenReturn(emptyFeed);
person Bartek Lipinski    schedule 22.02.2017
comment
Не могли бы вы объяснить это более подробно? Я пробовал это, но у меня все тот же NullPointerException в feedService.getFeeds().concatMap(). - person yadav_vi; 22.02.2017
comment
Конечно вещь. Вы вызываете when(feedRepository.getFeed(PAGE_NUMBER, CATEGORIES, LANGUAGE)).thenReturn(emptyFeed);, что означает, что вы пытаетесь смоделировать метод объекта feedRepository. Mockito не может имитировать методы реальных объектов. Mockito может делать это либо с полностью фиктивными объектами (используя @Mock или Mockito.mock), либо с обернутыми реальными объектами (используя Mockito.spy). - person Bartek Lipinski; 22.02.2017
comment
Единственный объект, над которым вы издевались, — это feedService. Вы создали feedRepository обычным способом (используя издевательский feedService). - person Bartek Lipinski; 22.02.2017
comment
Я удалил само предложение when/then для feedRepository и сделал when/then для feedService (как вы сказали, это то, что издевается, а не фидРепозиторий), и, похоже, это работает. - person yadav_vi; 22.02.2017
comment
feedRepository = Mockito.spy (FeedRepositoryImpl.getFeedRepository (feedService)); теперь работает. Я все еще получаю NullPointerException. - person yadav_vi; 22.02.2017
comment
вам нужно будет вставить код, который вы на самом деле используете. что такое newsFeedRepository - person Bartek Lipinski; 22.02.2017
comment
если вы издеваетесь над getFeed на feedService, а не на feedRepository, то вам больше не нужно spy этого делать. - person Bartek Lipinski; 22.02.2017
comment
Прошу прощения за это, newsFeedRepository и feedRepository это одна и та же переменная. Я отредактировал вопросы. - person yadav_vi; 22.02.2017
comment
Хорошо, я отредактировал ответ, чтобы мы были на одной странице. Если после изменения вы по-прежнему получаете NPE, попробуйте установить when/thenReturn перед созданием экземпляра feedRepository. - person Bartek Lipinski; 22.02.2017