Adding some unit tests

This commit is contained in:
Maxime NATUREL 2023-02-17 16:41:03 +01:00
parent b5af6f5a0f
commit 25ca598414
4 changed files with 138 additions and 30 deletions

View file

@ -127,7 +127,6 @@ class LiveLocationMapViewModel @AssistedInject constructor(
setState { copy(loadingMapHasFailed = true) } setState { copy(loadingMapHasFailed = true) }
} }
// TODO add unit tests
private fun handleZoomToUserLocation() = withState { state -> private fun handleZoomToUserLocation() = withState { state ->
if (!state.isLoadingUserLocation) { if (!state.isLoadingUserLocation) {
setState { setState {

View file

@ -20,13 +20,14 @@ import com.airbnb.mvrx.test.MavericksTestRule
import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationData
import im.vector.app.features.location.live.StopLiveLocationShareUseCase import im.vector.app.features.location.live.StopLiveLocationShareUseCase
import im.vector.app.test.fakes.FakeLocationSharingServiceConnection import im.vector.app.test.fakes.FakeLocationSharingServiceConnection
import im.vector.app.test.fakes.FakeLocationTracker
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.test import im.vector.app.test.test
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.unmockkAll import io.mockk.unmockkAll
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.After import org.junit.After
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -41,16 +42,20 @@ class LiveLocationMapViewModelTest {
private val args = LiveLocationMapViewArgs(roomId = A_ROOM_ID) private val args = LiveLocationMapViewArgs(roomId = A_ROOM_ID)
private val getListOfUserLiveLocationUseCase = mockk<GetListOfUserLiveLocationUseCase>() private val fakeSession = FakeSession()
private val locationServiceConnection = FakeLocationSharingServiceConnection() private val fakeGetListOfUserLiveLocationUseCase = mockk<GetListOfUserLiveLocationUseCase>()
private val stopLiveLocationShareUseCase = mockk<StopLiveLocationShareUseCase>() private val fakeLocationSharingServiceConnection = FakeLocationSharingServiceConnection()
private val fakeStopLiveLocationShareUseCase = mockk<StopLiveLocationShareUseCase>()
private val fakeLocationTracker = FakeLocationTracker()
private fun createViewModel(): LiveLocationMapViewModel { private fun createViewModel(): LiveLocationMapViewModel {
return LiveLocationMapViewModel( return LiveLocationMapViewModel(
LiveLocationMapViewState(args), LiveLocationMapViewState(args),
getListOfUserLiveLocationUseCase, session = fakeSession,
locationServiceConnection.instance, getListOfUserLiveLocationUseCase = fakeGetListOfUserLiveLocationUseCase,
stopLiveLocationShareUseCase locationSharingServiceConnection = fakeLocationSharingServiceConnection.instance,
stopLiveLocationShareUseCase = fakeStopLiveLocationShareUseCase,
locationTracker = fakeLocationTracker.instance,
) )
} }
@ -60,30 +65,94 @@ class LiveLocationMapViewModelTest {
} }
@Test @Test
fun `given the viewModel has been initialized then viewState contains user locations list`() = runTest { fun `given the viewModel has been initialized then viewState contains user locations list and location tracker is setup`() {
val userLocations = listOf( // Given
UserLiveLocationViewState( val userLocations = listOf(givenAUserLiveLocationViewState(userId = "@userId1:matrix.org"))
MatrixItem.UserItem(id = "@userId1:matrix.org", displayName = "User 1", avatarUrl = ""), fakeLocationSharingServiceConnection.givenBind()
pinDrawable = mockk(), every { fakeGetListOfUserLiveLocationUseCase.execute(A_ROOM_ID) } returns flowOf(userLocations)
locationData = LocationData(latitude = 1.0, longitude = 2.0, uncertainty = null),
endOfLiveTimestampMillis = 123,
locationTimestampMillis = 123,
showStopSharingButton = false
)
)
locationServiceConnection.givenBind()
every { getListOfUserLiveLocationUseCase.execute(A_ROOM_ID) } returns flowOf(userLocations)
// When
val viewModel = createViewModel() val viewModel = createViewModel()
viewModel val viewModelTest = viewModel.test()
.test()
// Then
viewModelTest
.assertState( .assertState(
LiveLocationMapViewState(args).copy( LiveLocationMapViewState(args).copy(
userLocations = userLocations userLocations = userLocations,
showLocateUserButton = true,
)
).finish()
fakeLocationSharingServiceConnection.verifyBind(viewModel)
fakeLocationTracker.verifyAddCallback(viewModel)
}
@Test
fun `given the viewModel when it is cleared cleanUp are done`() {
// Given
fakeLocationSharingServiceConnection.givenBind()
fakeLocationSharingServiceConnection.givenUnbind()
every { fakeGetListOfUserLiveLocationUseCase.execute(A_ROOM_ID) } returns flowOf(emptyList())
val viewModel = createViewModel()
// When
viewModel.onCleared()
// Then
fakeLocationSharingServiceConnection.verifyUnbind(viewModel)
fakeLocationTracker.verifyRemoveCallback(viewModel)
}
@Test
fun `given current user shares their live location then locate button should not be shown`() {
// Given
val userLocations = listOf(givenAUserLiveLocationViewState(userId = fakeSession.myUserId))
fakeLocationSharingServiceConnection.givenBind()
every { fakeGetListOfUserLiveLocationUseCase.execute(A_ROOM_ID) } returns flowOf(userLocations)
val viewModel = createViewModel()
// When
val viewModelTest = viewModel.test()
// Then
viewModelTest
.assertState(
LiveLocationMapViewState(args).copy(
userLocations = userLocations,
showLocateUserButton = false,
) )
) )
.finish() .finish()
locationServiceConnection.verifyBind(viewModel)
} }
@Test
fun `given current user does not share their live location then locate button should be shown`() {
// Given
val userLocations = listOf(givenAUserLiveLocationViewState(userId = "@userId1:matrix.org"))
fakeLocationSharingServiceConnection.givenBind()
every { fakeGetListOfUserLiveLocationUseCase.execute(A_ROOM_ID) } returns flowOf(userLocations)
val viewModel = createViewModel()
// When
val viewModelTest = viewModel.test()
// Then
viewModelTest
.assertState(
LiveLocationMapViewState(args).copy(
userLocations = userLocations,
showLocateUserButton = true,
)
)
.finish()
}
private fun givenAUserLiveLocationViewState(userId: String) = UserLiveLocationViewState(
MatrixItem.UserItem(id = userId, displayName = "User 1", avatarUrl = ""),
pinDrawable = mockk(),
locationData = LocationData(latitude = 1.0, longitude = 2.0, uncertainty = null),
endOfLiveTimestampMillis = 123,
locationTimestampMillis = 123,
showStopSharingButton = false
)
} }

View file

@ -17,10 +17,8 @@
package im.vector.app.test.fakes package im.vector.app.test.fakes
import im.vector.app.features.location.live.tracking.LocationSharingServiceConnection import im.vector.app.features.location.live.tracking.LocationSharingServiceConnection
import io.mockk.every import io.mockk.justRun
import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.runs
import io.mockk.verify import io.mockk.verify
class FakeLocationSharingServiceConnection { class FakeLocationSharingServiceConnection {
@ -28,10 +26,18 @@ class FakeLocationSharingServiceConnection {
val instance = mockk<LocationSharingServiceConnection>() val instance = mockk<LocationSharingServiceConnection>()
fun givenBind() { fun givenBind() {
every { instance.bind(any()) } just runs justRun { instance.bind(any()) }
} }
fun verifyBind(callback: LocationSharingServiceConnection.Callback) { fun verifyBind(callback: LocationSharingServiceConnection.Callback) {
verify { instance.bind(callback) } verify { instance.bind(callback) }
} }
fun givenUnbind() {
justRun { instance.unbind(any()) }
}
fun verifyUnbind(callback: LocationSharingServiceConnection.Callback) {
verify { instance.unbind(callback) }
}
} }

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.test.fakes
import im.vector.app.features.location.LocationTracker
import io.mockk.mockk
import io.mockk.verify
class FakeLocationTracker {
val instance: LocationTracker = mockk(relaxed = true)
fun verifyAddCallback(callback: LocationTracker.Callback) {
verify { instance.addCallback(callback) }
}
fun verifyRemoveCallback(callback: LocationTracker.Callback) {
verify { instance.removeCallback(callback) }
}
}