distinctUntilChanged를 사용하면 테스트가 잘되지 않았습니다.

18763 단어 안드로이드test
이 시험은 통과한다
class SampleViewModel : ViewModel() {

    private val _liveData = MutableLiveData<Boolean>()
    val liveData: LiveData<Boolean>
        get() = _liveData

    fun updateLiveData(bool: Boolean) {
        _liveData.value = bool
    }
}
class SampleViewModelTest {

    @get:Rule
    val rule = InstantTaskExecutorRule()

    @Test
    fun testUpdateLiveData() {

        val viewModel = SampleViewModel()

        assertThat(viewModel.liveData.value).isNull()

        viewModel.updateLiveData(true)

        assertThat(viewModel.liveData.value).isTrue()
    }
}

그러나 distinctUntilChanged를 사용하도록 liveData를 수정하면 assertThat(viewModel.liveData.value).isTrue()에서
class SampleViewModel : ViewModel() {

    private val _liveData = MutableLiveData<Boolean>()
    val liveData: LiveData<Boolean>
        get() = _liveData.distinctUntilChanged() // 修正

    fun updateLiveData(bool: Boolean) {
        _liveData.value = bool
    }
}

원인 1


distinctUntilChanged 구현을 살펴보면 MediatorLiveData를 새로 생성하고 소스를 추가하고 LiveData로 반환합니다.
즉, 앞의 코드에서는 liveData 를 취득하려고 할 때마다, 다른 LiveData 가 돌아오고 있었다.
또한 소스의 LiveData (_liveData) 값에 관계없이 초기 값이 null 인 MediatorLiveData가 생성되므로 이전 테스트에서는 항상 viewModel.liveData.value == null입니다.

Transformations.java
    ...

    @MainThread
    @NonNull
    public static <X> LiveData<X> distinctUntilChanged(@NonNull LiveData<X> source) {
        final MediatorLiveData<X> outputLiveData = new MediatorLiveData<>();
        outputLiveData.addSource(source, new Observer<X>() {

            boolean mFirstTime = true;

            @Override
            public void onChanged(X currentValue) {
                final X previousValue = outputLiveData.getValue();
                if (mFirstTime
                        || (previousValue == null && currentValue != null)
                        || (previousValue != null && !previousValue.equals(currentValue))) {
                    mFirstTime = false;
                    outputLiveData.setValue(currentValue);
                }
            }
        });
        return outputLiveData;
    }

    ...

대응



게터를 사용하지 않고 필드에 보관하십시오.
class SampleViewModel : ViewModel() {

    private val _liveData = MutableLiveData<Boolean>()
    val liveData: LiveData<Boolean> = _liveData.distinctUntilChanged() // 修正

    fun updateLiveData(bool: Boolean) {
        _liveData.value = bool
    }
}

원인 2


addSource 의 구현을 보면(자), 마지막으로 hasActiveObservers 로 MediatorLiveData 가 액티브한 Observer 를 가지고 있는지 어떤지를 확인해, 가지고 있는 경우만 plug 를 호출하고 있다.

MediatorLiveData.java
    ...

    @MainThread
    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
        Source<S> e = new Source<>(source, onChanged);
        Source<?> existing = mSources.putIfAbsent(source, e);
        if (existing != null && existing.mObserver != onChanged) {
            throw new IllegalArgumentException(
                    "This source was already added with the different observer");
        }
        if (existing != null) {
            return;
        }
        if (hasActiveObservers()) {
            e.plug();
        }
    }

    ...
plug의 구현을 살펴보면 소스 LiveData를 observe하고 있습니다.
즉, MediatorLiveData ( liveData )가, 액티브한 Observer에 의해 observe 되어 있지 않은 경우, 소스의 LiveData ( _liveData )의 값의 변경을 받지 않는 구현이 되어 있다.
따라서 이전 코드에서는 liveData 값이 업데이트되지 않고 초기 값 null로 유지됩니다.

MediatorLiveData.java
    ...

    private static class Source<V> implements Observer<V> {
        final LiveData<V> mLiveData;
        final Observer<? super V> mObserver;
        int mVersion = START_VERSION;

        Source(LiveData<V> liveData, final Observer<? super V> observer) {
            mLiveData = liveData;
            mObserver = observer;
        }

        void plug() {
            mLiveData.observeForever(this);
        }

        void unplug() {
            mLiveData.removeObserver(this);
        }

        @Override
        public void onChanged(@Nullable V v) {
            if (mVersion != mLiveData.getVersion()) {
                mVersion = mLiveData.getVersion();
                mObserver.onChanged(v);
            }
        }
    }

    ...

대응



테스트 내에서 liveDataobserve합니다.
class SampleViewModelTest {

    @get:Rule
    val rule = InstantTaskExecutorRule()

    @Test
    fun testUpdateLiveData() {

        val viewModel = SampleViewModel()
        viewModel.liveData.observeForever {} // 追加

        assertThat(viewModel.liveData.value).isNull()

        viewModel.updateLiveData(true)

        assertThat(viewModel.liveData.value).isTrue()
    }
}

요약



MutableLiveData를 사용할 때, 게터를 이용하는 경우도 있다고 생각하지만, distinctUntilChanged 를 사용하는 경우는, 필드로 보관 유지하는 것이 좋다.

또한 distinctUntilChanged, map, switchMap 등 MediatorLiveData를 사용할 때는 observe하지 않으면 값이 업데이트되지 않습니다.

좋은 웹페이지 즐겨찾기