Make mock test working

This commit is contained in:
Sylwester Zieliński
2023-06-19 12:58:18 +02:00
parent bcd481307c
commit dfd307a698
5 changed files with 97 additions and 63 deletions

View File

@@ -66,12 +66,11 @@ dependencies {
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.lifecycle.service)
testImplementation(libs.junit4)
testImplementation("io.mockk:mockk:1.13.5")
testImplementation(libs.test.mockk)
testImplementation(libs.androidx.test.ext)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation("org.slf4j:slf4j-simple:2.0.5")
testImplementation("org.robolectric:robolectric:4.10.3")
testImplementation(libs.test.slf4j.simple)
testImplementation(libs.test.robolectric)
testImplementation(libs.kotlin.junit)
}

View File

@@ -38,17 +38,24 @@ class GlsServer @Inject constructor(
private val scope: CoroutineScope
) {
lateinit var server: BleGattServer
lateinit var glsCharacteristic: BleServerGattCharacteristic
lateinit var glsContextCharacteristic: BleServerGattCharacteristic
lateinit var racpCharacteristic: BleServerGattCharacteristic
lateinit var batteryLevelCharacteristic: BleServerGattCharacteristic
private var lastRequest = byteArrayOf()
val YOUNGEST_RECORD = byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11)
val OLDEST_RECORD = byteArrayOf(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11)
val records = listOf(
byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11),
YOUNGEST_RECORD,
byteArrayOf(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11),
byteArrayOf(0x07, 0x02, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0B, 0x00, 0x00, 0x54, 0xD2.toByte(), 0x11),
byteArrayOf(0x07, 0x03, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0E, 0x00, 0x00, 0x6B, 0xD2.toByte(), 0x11),
byteArrayOf(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11)
OLDEST_RECORD
)
val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01)
@@ -96,7 +103,7 @@ class GlsServer @Inject constructor(
listOf(batteryLevelCharacteristic)
)
val server = BleGattServer.create(
server = BleGattServer.create(
context = context,
config = arrayOf(serviceConfig, batteryService),
mock = device
@@ -112,6 +119,10 @@ class GlsServer @Inject constructor(
}
}
internal fun stopServer() {
server.stopServer()
}
private fun setUpConnection(connection: BluetoothGattServerConnection) {
val glsService = connection.services.findService(GLS_SERVICE_UUID)!!
glsCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!!
@@ -123,27 +134,33 @@ class GlsServer @Inject constructor(
startGlsService(connection)
startBatteryService(connection)
// startBatteryService(connection)
}
private fun startGlsService(connection: BluetoothGattServerConnection) {
racpCharacteristic.value
.filter { it.isNotEmpty() }
.onEach {
if (it.contentEquals(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value)) {
sendAll(glsCharacteristic)
racpCharacteristic.setValue(racp)
} else if (it.contentEquals(RecordAccessControlPointInputParser.reportLastStoredRecord().value)) {
sendLast(glsCharacteristic)
racpCharacteristic.setValue(racp)
} else if (it.contentEquals(RecordAccessControlPointInputParser.reportFirstStoredRecord().value)) {
sendFirst(glsCharacteristic)
racpCharacteristic.setValue(racp)
}
}
.onEach { lastRequest = it }
.launchIn(scope)
}
internal fun continueWithResponse() {
sendResponse(lastRequest)
}
private fun sendResponse(request: ByteArray) {
if (request.contentEquals(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value)) {
sendAll(glsCharacteristic)
racpCharacteristic.setValue(racp)
} else if (request.contentEquals(RecordAccessControlPointInputParser.reportLastStoredRecord().value)) {
sendLast(glsCharacteristic)
racpCharacteristic.setValue(racp)
} else if (request.contentEquals(RecordAccessControlPointInputParser.reportFirstStoredRecord().value)) {
sendFirst(glsCharacteristic)
racpCharacteristic.setValue(racp)
}
}
private fun sendFirst(characteristics: BleServerGattCharacteristic) {
characteristics.setValue(records.first())
}

View File

@@ -49,6 +49,7 @@ import kotlinx.coroutines.launch
import no.nordicsemi.android.analytics.AppAnalytics
import no.nordicsemi.android.analytics.Profile
import no.nordicsemi.android.analytics.ProfileConnectedEvent
import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher
import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.gls.GlsDetailsDestinationId
@@ -67,9 +68,6 @@ import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus
import no.nordicsemi.android.common.logger.BlekLogger
import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher
import no.nordicsemi.android.kotlin.ble.core.ext.toDisplayString
import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser
import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementContextParser
import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser
@@ -239,7 +237,6 @@ internal class GLSViewModel @Inject constructor(
}
private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) = viewModelScope.launch {
println("AAA: Response code: ${data}")
when (data) {
is NumberOfRecordsData -> onNumberOfRecordsReceived(data.numberOfRecords)
is ResponseData -> when (data.responseCode) {

View File

@@ -12,7 +12,6 @@ import io.mockk.mockkStatic
import io.mockk.spyk
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
@@ -28,19 +27,21 @@ import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected
import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel
import no.nordicsemi.android.kotlin.ble.client.main.ClientScope
import no.nordicsemi.android.kotlin.ble.core.MockServerDevice
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus
import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser
import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus
import no.nordicsemi.android.kotlin.ble.server.main.ServerScope
import no.nordicsemi.android.ui.view.NordicLoggerFactory
import no.nordicsemi.android.ui.view.StringConst
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertContentEquals
/**
* Example local unit test, which will execute on the development machine (host).
@@ -93,6 +94,7 @@ internal class GLSViewModelTest {
every { ClientScope } returns CoroutineScope(UnconfinedTestDispatcher())
mockkStatic("no.nordicsemi.android.kotlin.ble.server.main.ServerScopeKt")
every { ServerScope } returns CoroutineScope(UnconfinedTestDispatcher())
every { stringConst.APP_NAME } returns "Test"
viewModel = spyk(GLSViewModel(context, navigator, analytics, stringConst, object :
NordicLoggerFactory {
@@ -106,6 +108,8 @@ internal class GLSViewModelTest {
}
}))
justRun { viewModel.logAnalytics(any()) }
glsServer = GlsServer(CoroutineScope(UnconfinedTestDispatcher()))
glsServer.start(spyk(), device)
}
@@ -123,54 +127,71 @@ internal class GLSViewModelTest {
}
@Test
fun checkOnClick() = runTest {
// every { viewModel.recordAccessControlPointCharacteristic } returns characteristic
// coJustRun { characteristic.write(any(), any()) }
viewModel.onEvent(OnWorkingModeSelected(WorkingMode.FIRST))
advanceUntilIdle()
assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus)
}
@Test
fun `when connection fails return disconnected`() {
val serverDevice = mockk<ServerDevice>()
fun `when connection fails return disconnected`() = runTest {
val disconnectedState = GattConnectionStateWithStatus(
GattConnectionState.STATE_DISCONNECTED,
BleGattConnectionStatus.SUCCESS
)
// every { viewModel.recordAccessControlPointCharacteristic } returns characteristic
// coJustRun { characteristic.write(any(), any()) }
mockkStatic("no.nordicsemi.android.kotlin.ble.client.main.ClientDeviceExtKt")
every { serverDevice.name } returns "Test"
every { serverDevice.address } returns "11:22:33:44:55"
every { stringConst.APP_NAME } returns "Test"
viewModel.handleResult(NavigationResult.Success(device))
glsServer.stopServer()
viewModel.handleResult(NavigationResult.Success(serverDevice))
advanceUntilIdle()
assertEquals(disconnectedState, viewModel.state.value.glsServiceData.connectionState)
}
@Test
fun checkOnClick2() = runTest {
every { stringConst.APP_NAME } returns "Test"
justRun { viewModel.logAnalytics(any()) }
fun `when request first record then change status and get 1 record`() = runTest {
viewModel.handleResult(NavigationResult.Success(device))
advanceUntilIdle() //Needed because of delay() in waitForBonding()
assertEquals(RequestStatus.IDLE, viewModel.state.value.glsServiceData.requestStatus)
advanceUntilIdle()
delay(1000)
viewModel.onEvent(OnWorkingModeSelected(WorkingMode.FIRST))
assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus)
// advanceUntilIdle()
// assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus)
//
//// glsServer.glsCharacteristic.setValue(glsServer.records.first())
// glsServer.racpCharacteristic.setValue(glsServer.racp)
//
// advanceUntilIdle()
//
// assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus)
glsServer.continueWithResponse() //continue server breakpoint
assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus)
assertEquals(1, viewModel.state.value.glsServiceData.records.size)
val parsedResponse = GlucoseMeasurementParser.parse(glsServer.YOUNGEST_RECORD)
assertEquals(parsedResponse, viewModel.state.value.glsServiceData.records.keys.first())
}
@Test
fun `when request last record then change status and get 1 record`() = runTest {
viewModel.handleResult(NavigationResult.Success(device))
advanceUntilIdle() //Needed because of delay() in waitForBonding()
assertEquals(RequestStatus.IDLE, viewModel.state.value.glsServiceData.requestStatus)
viewModel.onEvent(OnWorkingModeSelected(WorkingMode.LAST))
assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus)
glsServer.continueWithResponse() //continue server breakpoint
assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus)
assertEquals(1, viewModel.state.value.glsServiceData.records.size)
val parsedResponse = GlucoseMeasurementParser.parse(glsServer.OLDEST_RECORD)
assertEquals(parsedResponse, viewModel.state.value.glsServiceData.records.keys.first())
}
@Test
fun `when request all record then change status and get 5 records`() = runTest {
viewModel.handleResult(NavigationResult.Success(device))
advanceUntilIdle() //Needed because of delay() in waitForBonding()
assertEquals(RequestStatus.IDLE, viewModel.state.value.glsServiceData.requestStatus)
viewModel.onEvent(OnWorkingModeSelected(WorkingMode.ALL))
assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus)
glsServer.continueWithResponse() //continue server breakpoint
advanceUntilIdle() //We have to use because of delay() in sendAll()
assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus)
assertEquals(5, viewModel.state.value.glsServiceData.records.size)
val expectedRecords = glsServer.records.map { GlucoseMeasurementParser.parse(it) }
assertContentEquals(expectedRecords, viewModel.state.value.glsServiceData.records.keys)
}
}

View File

@@ -50,7 +50,7 @@ dependencyResolutionManagement {
}
versionCatalogs {
create("libs") {
from("no.nordicsemi.android.gradle:version-catalog:1.5.5")
from("no.nordicsemi.android.gradle:version-catalog:1.5.8")
}
}
}