2016-12-30 3 views
0
void start() { 
    bar.foo() 
     .filter(i -> i % 2 == 0) 
     .subscribeOn(computation()) 
     .observeOn(io()) 
     .subscribe(new FooSubscriber()); 
} 

В этой функции я вижу 3 очка испытания:Как я могу проверить этот код rxJava?

  1. Убедитесь, что я называю bar.foo().
  2. Проверьте правильность ввода filter.
  3. Подтвердите, что я подписан на bar.foo().

Первый пункт легко проверить с помощью Mockito.verify(). Третий момент я могу ввести Планировщиков и использовать Schedulers.immediate(), а затем издеваться над наблюдателем с темой и проверить Subject.hasObservers(). Но я понятия не имею, как проверить второй пункт.

Как я могу проверить этот код? Должен ли я реорганизовать его? Как?

Пожалуйста, подумайте, что filter - это всего лишь пример, у меня есть большая цепочка с различными операторами.

+0

Я никогда бы не проверить, что вы называете 'bar.foo()'; это прямо там, как первое в вашем коде, если это не работает, когда вы вызываете 'start()' у вас гораздо больше проблем, о которых нужно беспокоиться. (если только это не * первое * ...) –

ответ

1

Трудно проверить этот метод, так как нет никакого «наблюдаемого» поведения для утверждения, и вам нужно получить ваш тестовый код «на пути» логики.

Вот простой подход можно следовать: (хотя вы могли бы хотеть рассмотреть ломать вещи, чтобы облегчить тестирование)

  1. Mock Foo, убедитесь, что панель() вызывается, если вам нужно, верните реальное значение Observable из bar(), которое будет разматывать цепочку обратного вызова при вызове subscribe. - это проверит, что ваша цепочка подключена так, как ожидалось.

  2. INJECT планировщики, которые выполняют логику в основном потоке в блокирующем способом, таким образом, сохраняя испытания синхронных и легко понять

  3. Экстракт new FooSubscriber() к частному способу упаковки и использовать Mockito шпионить новый метод, возвращая тестовый абонент, который делает утверждения по отфильтрованным данным, испускаемым из наблюдаемого или - вводит фабричный класс, который создает экземпляры FooSubscriber, которые вы можете высмеивать для целей тестирования, возвращая тестового подписчика. - в основном жесткое кодирование нового ключевого слова блокирует вас от проверки поведения.

Я могу привести пример, если вам нужно, надеюсь, что это вас поймает.

EDIT: пример обоих способов, описанных выше:

package com.rx; 

import org.junit.Assert; 
import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.Mockito; 
import org.mockito.runners.MockitoJUnitRunner; 
import rx.Observable; 
import rx.Observer; 
import rx.Scheduler; 
import rx.schedulers.Schedulers; 

import java.util.ArrayList; 
import java.util.List; 

@RunWith(MockitoJUnitRunner.class) 
public class TestRxMethod { 

    // prod Bar class - this class tested in isolation in different test. 
    public static class Bar { 

     public Observable<Integer> foo() { 
      return null; 
     } 
    } 

    // prod FooSubscriber class - this class tested in isolation in different test. 
    public static class FooSubscriber implements Observer<Integer> { 

     @Override 
     public void onCompleted() { 
     } 

     @Override 
     public void onError(Throwable e) { 
     } 

     @Override 
     public void onNext(Integer t) { 
     } 
    } 

    // prod FooSubscriberFactory class - this class tested in isolation in different test. 
    public static class FooSubscriberFactory { 

     public Observer<Integer> getInstance() { 
      return null; 
     } 
    } 

    // prod "class under test" 
    public static class UnderTest { 
     private final Bar bar; 
     private final Scheduler computationScheduler; 
     private final Scheduler ioScheduler; 
     private final FooSubscriberFactory fooSubscriberFactory; 

     public UnderTest(Bar bar, Scheduler computationScheduler, Scheduler ioScheduler, 
       FooSubscriberFactory fooSubscriberFactory) { 
      this.bar = bar; 
      this.computationScheduler = computationScheduler; 
      this.ioScheduler = ioScheduler; 
      this.fooSubscriberFactory = fooSubscriberFactory; 
     } 

     public void start() { 
      //@formatter:off 
      bar.foo() 
       .filter(i -> i.intValue() % 2 == 0) 
       .subscribeOn(computationScheduler) 
       .observeOn(ioScheduler) 
       .subscribe(fooSubscriber()); 
      //@formatter:on 
     } 

     // package private so can be overridden by unit test some drawbacks 
     // using this strategy like class cant be made final. - use only 
     // if cant restructure code. 
     Observer<Integer> fooSubscriber() { 
      return fooSubscriberFactory.getInstance(); 
     } 
    } 

    // test Foo subscriber class - test will put set an instance of 
    // this class as the observer on the callback chain. 
    public static class TestFooSubscriber implements Observer<Integer> { 

     public List<Integer> filteredIntegers = new ArrayList<>(); 

     @Override 
     public void onCompleted() { 
      // noop 
     } 

     @Override 
     public void onError(Throwable e) { 
      // noop 
     } 

     @Override 
     public void onNext(Integer i) { 
      // aggregate filtered integers for later assertions 
      filteredIntegers.add(i); 
     } 
    } 

    // mock bar for test 
    private Bar bar; 

    // mock foo subscriber factory for test 
    private FooSubscriberFactory fooSubscriberFactory; 

    // class under test - injected with test dependencies 
    private UnderTest underTest; 

    @Before 
    public void setup() { 
     bar = Mockito.mock(Bar.class); 
     fooSubscriberFactory = Mockito.mock(FooSubscriberFactory.class); 
     underTest = new UnderTest(bar, Schedulers.immediate(), Schedulers.immediate(), fooSubscriberFactory); 
    } 

    // Option #1 - injecting a factory 
    @Test 
    public void start_shouldWork_usingMockedFactory() { 
     // setup bar mock to emit integers 
     Mockito.when(bar.foo()).thenReturn(Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); 

     // setup the subscriber factory to produce an instance of the test subscriber 
     TestFooSubscriber testSubscriber = new TestFooSubscriber(); 
     Mockito.when(fooSubscriberFactory.getInstance()).thenReturn(testSubscriber); 

     underTest.start(); 

     Assert.assertEquals(5, testSubscriber.filteredIntegers.size()); 
     // ... add more assertions as needed per the use cases ... 
    } 

    // Option #2 - spying a protected method 
    @Test 
    public void start_shouldWork_usingSpyMethod() { 
     // setup bar mock to emit integers 
     Mockito.when(bar.foo()).thenReturn(Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); 

     // spy the class under test (use only as a last resort option) 
     underTest = Mockito.spy(underTest); 
     TestFooSubscriber testSubscriber = new TestFooSubscriber(); 
     Mockito.when(underTest.fooSubscriber()).thenReturn(testSubscriber); 

     underTest.start(); 

     Assert.assertEquals(5, testSubscriber.filteredIntegers.size()); 
     // ... add more assertions as needed per the use cases ... 
    } 
} 
+0

Вы говорите о том, чтобы сломать вещи, чтобы сделать тест проще. Как бы вы нарушили этот код, чтобы сделать его проще? –

Смежные вопросы