distinctUntilChanged를 사용하면 테스트가 잘되지 않았습니다.
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);
}
}
}
...
대응
테스트 내에서 liveData
를 observe
합니다.
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
하지 않으면 값이 업데이트되지 않습니다.
Reference
이 문제에 관하여(distinctUntilChanged를 사용하면 테스트가 잘되지 않았습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/penu/items/a72f2b89106f6e7e17b2
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
...
@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
}
}
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);
}
}
}
...
대응
테스트 내에서
liveData
를 observe
합니다.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
하지 않으면 값이 업데이트되지 않습니다.
Reference
이 문제에 관하여(distinctUntilChanged를 사용하면 테스트가 잘되지 않았습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/penu/items/a72f2b89106f6e7e17b2
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(distinctUntilChanged를 사용하면 테스트가 잘되지 않았습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/penu/items/a72f2b89106f6e7e17b2텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)