RxJava 风格的 LiveData TestObserver 和测试扩展因 NullPointerException 而失败
RxJava style LiveData TestObserver and test extension fails with NullPointerException
我创建了一个 TestObserver
class 类似 RxJava 的对应物和 LiveData 扩展函数来测试 LiveData 的多个值,例如像 LOADING
这样的状态和作为动作的结果 ERROR
或 SUCCESS
.
class TestObserver<T>(private val liveData: LiveData<T>) : Observer<T> {
init {
liveData.observeForever(this)
}
private val testValues = mutableListOf<T>()
override fun onChanged(t: T) {
if (t != null) testValues.add(t)
}
fun testAssertNoValues(): TestObserver<T> {
if (testValues.isNotEmpty()) throw AssertionException("Assertion error with actual size ${testValues.size}")
return this
}
fun testAssertValueCount(count: Int): TestObserver<T> {
if (count < 0) throw AssertionException("Assert count cannot be smaller than zero")
if (count != testValues.size) throw AssertionException("Assertion error with expected $count while actual ${testValues.size}")
return this
}
fun assertValues(vararg predicates: T): TestObserver<T> {
predicates.forEach { predicate ->
testValues.forEach { testValue ->
if (predicate != testValue) throw Exception("Assertion error")
}
}
return this
}
fun assertValues(predicate: List<T>.() -> Boolean): TestObserver<T> {
testValues.predicate()
return this
}
fun values(predicate: List<T>.() -> Unit): TestObserver<T> {
testValues.predicate()
return this
}
fun values(): List<T> {
return testValues
}
fun dispose() {
testValues.clear()
liveData.removeObserver(this)
}
}
fun <T> LiveData<T>.test(): TestObserver<T> {
val testObserver = TestObserver(this)
observeForever(testObserver)
return testObserver
}
class AssertionException(message: String) : Exception(message)
并在样本测试中使用它来检查它是否有效
@Test
fun test() = testCoroutineRule.runBlockingTest {
val myTestData = MutableLiveData<Int>()
myTestData.value = 1
myTestData.value = 2
myTestData.value = 3
myTestData.test().values {
this.forEach {
println(" Test numbers: $it")
}
}
}
但是 mutableList 是 null 我想知道为什么?
在观察之前调用 liveData.removeObserver(this)
解决了问题。并对完整实现做了一些更改。
/**
* RxJava style [Observer] for [LiveData] to test multiple values or states in container.
*
* This class is useful for testing view or action states or order of states if you are using
* stateful machine.
*
* * Use with `InstantTaskExecutorRule` or a similar mechanism to execute tasks synchronously.
*
*/
class LiveDataTestObserver<T> constructor(
private val liveData: LiveData<T>
) : Observer<T> {
init {
liveData.observeForever(this)
}
private val testValues = mutableListOf<T>()
override fun onChanged(t: T) {
if (t != null) testValues.add(t)
}
fun assertNoValues(): LiveDataTestObserver<T> {
if (testValues.isNotEmpty()) throw AssertionError(
"Assertion error with actual size ${testValues.size}"
)
return this
}
fun assertValueCount(count: Int): LiveDataTestObserver<T> {
if (count < 0) throw AssertionError(
"Assertion error! value count cannot be smaller than zero"
)
if (count != testValues.size) throw AssertionError(
"Assertion error! with expected $count while actual ${testValues.size}"
)
return this
}
fun assertValues(vararg predicates: T): LiveDataTestObserver<T> {
if (!testValues.containsAll(predicates.asList())) throw AssertionError("Assertion error!")
return this
}
fun assertValues(predicate: (List<T>) -> Boolean): LiveDataTestObserver<T> {
predicate(testValues)
return this
}
fun values(predicate: (List<T>) -> Unit): LiveDataTestObserver<T> {
predicate(testValues)
return this
}
fun values(): List<T> {
return testValues
}
/**
* Removes this observer from the [LiveData] which was observing
*/
fun dispose() {
liveData.removeObserver(this)
}
/**
* Clears data available in this observer and removes this observer from the [LiveData] which was observing
*/
fun clear() {
testValues.clear()
dispose()
}
}
fun <T> LiveData<T>.test(): LiveDataTestObserver<T> {
val testObserver = LiveDataTestObserver(this)
// Remove this testObserver that is added in init block of TestObserver, and clears previous data
testObserver.clear()
observeForever(testObserver)
return testObserver
}
我创建了一个 TestObserver
class 类似 RxJava 的对应物和 LiveData 扩展函数来测试 LiveData 的多个值,例如像 LOADING
这样的状态和作为动作的结果 ERROR
或 SUCCESS
.
class TestObserver<T>(private val liveData: LiveData<T>) : Observer<T> {
init {
liveData.observeForever(this)
}
private val testValues = mutableListOf<T>()
override fun onChanged(t: T) {
if (t != null) testValues.add(t)
}
fun testAssertNoValues(): TestObserver<T> {
if (testValues.isNotEmpty()) throw AssertionException("Assertion error with actual size ${testValues.size}")
return this
}
fun testAssertValueCount(count: Int): TestObserver<T> {
if (count < 0) throw AssertionException("Assert count cannot be smaller than zero")
if (count != testValues.size) throw AssertionException("Assertion error with expected $count while actual ${testValues.size}")
return this
}
fun assertValues(vararg predicates: T): TestObserver<T> {
predicates.forEach { predicate ->
testValues.forEach { testValue ->
if (predicate != testValue) throw Exception("Assertion error")
}
}
return this
}
fun assertValues(predicate: List<T>.() -> Boolean): TestObserver<T> {
testValues.predicate()
return this
}
fun values(predicate: List<T>.() -> Unit): TestObserver<T> {
testValues.predicate()
return this
}
fun values(): List<T> {
return testValues
}
fun dispose() {
testValues.clear()
liveData.removeObserver(this)
}
}
fun <T> LiveData<T>.test(): TestObserver<T> {
val testObserver = TestObserver(this)
observeForever(testObserver)
return testObserver
}
class AssertionException(message: String) : Exception(message)
并在样本测试中使用它来检查它是否有效
@Test
fun test() = testCoroutineRule.runBlockingTest {
val myTestData = MutableLiveData<Int>()
myTestData.value = 1
myTestData.value = 2
myTestData.value = 3
myTestData.test().values {
this.forEach {
println(" Test numbers: $it")
}
}
}
但是 mutableList 是 null 我想知道为什么?
在观察之前调用 liveData.removeObserver(this)
解决了问题。并对完整实现做了一些更改。
/**
* RxJava style [Observer] for [LiveData] to test multiple values or states in container.
*
* This class is useful for testing view or action states or order of states if you are using
* stateful machine.
*
* * Use with `InstantTaskExecutorRule` or a similar mechanism to execute tasks synchronously.
*
*/
class LiveDataTestObserver<T> constructor(
private val liveData: LiveData<T>
) : Observer<T> {
init {
liveData.observeForever(this)
}
private val testValues = mutableListOf<T>()
override fun onChanged(t: T) {
if (t != null) testValues.add(t)
}
fun assertNoValues(): LiveDataTestObserver<T> {
if (testValues.isNotEmpty()) throw AssertionError(
"Assertion error with actual size ${testValues.size}"
)
return this
}
fun assertValueCount(count: Int): LiveDataTestObserver<T> {
if (count < 0) throw AssertionError(
"Assertion error! value count cannot be smaller than zero"
)
if (count != testValues.size) throw AssertionError(
"Assertion error! with expected $count while actual ${testValues.size}"
)
return this
}
fun assertValues(vararg predicates: T): LiveDataTestObserver<T> {
if (!testValues.containsAll(predicates.asList())) throw AssertionError("Assertion error!")
return this
}
fun assertValues(predicate: (List<T>) -> Boolean): LiveDataTestObserver<T> {
predicate(testValues)
return this
}
fun values(predicate: (List<T>) -> Unit): LiveDataTestObserver<T> {
predicate(testValues)
return this
}
fun values(): List<T> {
return testValues
}
/**
* Removes this observer from the [LiveData] which was observing
*/
fun dispose() {
liveData.removeObserver(this)
}
/**
* Clears data available in this observer and removes this observer from the [LiveData] which was observing
*/
fun clear() {
testValues.clear()
dispose()
}
}
fun <T> LiveData<T>.test(): LiveDataTestObserver<T> {
val testObserver = LiveDataTestObserver(this)
// Remove this testObserver that is added in init block of TestObserver, and clears previous data
testObserver.clear()
observeForever(testObserver)
return testObserver
}